Compare commits
1 Commits
972fd39da2
...
feat/svelt
Author | SHA1 | Date | |
---|---|---|---|
5c1c8c480b |
@@ -1,4 +1,4 @@
|
||||
FROM node:22
|
||||
FROM node:21
|
||||
|
||||
# IMAGE CUSTOMISATIONS
|
||||
|
||||
|
@@ -5,7 +5,6 @@
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="%sveltekit.assets%/svelte.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<script defer src="https://umami.max-richter.dev/script.js" data-website-id="585c442b-0524-4874-8955-f9853b44b17e"></script>
|
||||
%sveltekit.head%
|
||||
<title>Nodes</title>
|
||||
<script>
|
||||
@@ -15,7 +14,7 @@
|
||||
var value = JSON.parse(store);
|
||||
var themes = ["dark", "light", "catppuccin"];
|
||||
if (themes[value.theme]) {
|
||||
document.body.classList.add("theme-" + themes[value.theme]);
|
||||
document.documentElement.classList.add("theme-" + themes[value.theme]);
|
||||
}
|
||||
} catch (e) { }
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import type { NodeDefinition, NodeRegistry } from "@nodes/types";
|
||||
import { onMount } from "svelte";
|
||||
import { onDestroy, onMount } from "svelte";
|
||||
|
||||
let mx = $state(0);
|
||||
let my = $state(0);
|
||||
@@ -39,9 +39,10 @@
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
wrapper?.parentElement?.setAttribute("style", "cursor:help !important");
|
||||
const style = wrapper.parentElement?.style;
|
||||
style?.setProperty("cursor", "help");
|
||||
return () => {
|
||||
wrapper?.parentElement?.style.removeProperty("cursor");
|
||||
style?.removeProperty("cursor");
|
||||
};
|
||||
});
|
||||
</script>
|
||||
@@ -91,8 +92,9 @@
|
||||
border-radius: 5px;
|
||||
top: 10px;
|
||||
left: 10px;
|
||||
max-width: 250px;
|
||||
border: 1px solid var(--outline);
|
||||
z-index: 1000;
|
||||
z-index: 10000;
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
@@ -1,10 +1,10 @@
|
||||
<script lang="ts">
|
||||
import { T } from "@threlte/core";
|
||||
|
||||
import BackgroundVert from "./Background.vert";
|
||||
import BackgroundFrag from "./Background.frag";
|
||||
import { colors } from "../graph/state.svelte";
|
||||
import { colors } from "../graph/colors.svelte";
|
||||
import { Color } from "three";
|
||||
import { appSettings } from "$lib/settings/app-settings.svelte";
|
||||
|
||||
type Props = {
|
||||
minZoom: number;
|
||||
@@ -42,10 +42,10 @@
|
||||
value: [0, 1, 0],
|
||||
},
|
||||
backgroundColor: {
|
||||
value: new Color(0x171717),
|
||||
value: colors["layer-0"].clone(),
|
||||
},
|
||||
lineColor: {
|
||||
value: new Color(0x111111),
|
||||
value: colors["outline"].clone(),
|
||||
},
|
||||
zoomLimits: {
|
||||
value: [2, 50],
|
||||
@@ -55,8 +55,9 @@
|
||||
},
|
||||
}}
|
||||
uniforms.camPos.value={cameraPosition}
|
||||
uniforms.backgroundColor.value={$colors["layer-0"]}
|
||||
uniforms.lineColor.value={$colors["outline"]}
|
||||
uniforms.backgroundColor.value={appSettings.theme &&
|
||||
colors["layer-0"].clone()}
|
||||
uniforms.lineColor.value={appSettings.theme && colors["outline"].clone()}
|
||||
uniforms.zoomLimits.value={[minZoom, maxZoom]}
|
||||
uniforms.dimensions.value={[width, height]}
|
||||
/>
|
||||
|
@@ -1,13 +1,15 @@
|
||||
<script module lang="ts">
|
||||
import { colors } from "../graph/state.svelte";
|
||||
import { colors } from "../graph/colors.svelte";
|
||||
|
||||
const circleMaterial = new MeshBasicMaterial({
|
||||
color: get(colors).edge,
|
||||
color: colors.edge.clone(),
|
||||
toneMapped: false,
|
||||
});
|
||||
|
||||
colors.subscribe((c) => {
|
||||
circleMaterial.color.copy(c.edge.clone().convertSRGBToLinear());
|
||||
$effect.root(() => {
|
||||
$effect(() => {
|
||||
appSettings.theme;
|
||||
circleMaterial.color = colors.edge.clone().convertSRGBToLinear();
|
||||
})
|
||||
});
|
||||
|
||||
const lineCache = new Map<number, BufferGeometry>();
|
||||
@@ -23,23 +25,24 @@
|
||||
<script lang="ts">
|
||||
import { T } from "@threlte/core";
|
||||
import { MeshLineMaterial } from "@threlte/extras";
|
||||
import { BufferGeometry, MeshBasicMaterial, Vector3 } from "three";
|
||||
import { BufferGeometry, MeshBasicMaterial, Vector3 } from "three";
|
||||
import { CubicBezierCurve } from "three/src/extras/curves/CubicBezierCurve.js";
|
||||
import { Vector2 } from "three/src/math/Vector2.js";
|
||||
import { createEdgeGeometry } from "./createEdgeGeometry.js";
|
||||
import { get } from "svelte/store";
|
||||
import { appSettings } from "$lib/settings/app-settings.svelte";
|
||||
|
||||
type Props = {
|
||||
from: { x: number; y: number };
|
||||
to: { x: number; y: number };
|
||||
z:number;
|
||||
};
|
||||
|
||||
const { from, to }: Props = $props();
|
||||
|
||||
let samples = 5;
|
||||
const { from, to, z }: Props = $props();
|
||||
|
||||
let geometry: BufferGeometry|null = $state(null);
|
||||
|
||||
const lineColor = $derived(appSettings.theme && colors.edge.clone().convertSRGBToLinear());
|
||||
|
||||
let lastId: number | null = null;
|
||||
|
||||
const primeA = 31;
|
||||
@@ -63,7 +66,8 @@
|
||||
const length = Math.floor(
|
||||
Math.sqrt(Math.pow(new_x, 2) + Math.pow(new_y, 2)) / 4,
|
||||
);
|
||||
samples = Math.min(Math.max(10, length), 60) * 2;
|
||||
|
||||
const samples = Math.max(length * 16, 10);
|
||||
|
||||
curve.v0.set(0, 0);
|
||||
curve.v1.set(mid.x, 0);
|
||||
@@ -79,13 +83,12 @@
|
||||
lineCache.set(curveId, geometry);
|
||||
};
|
||||
|
||||
|
||||
$effect(() => {
|
||||
if (from || to) {
|
||||
update();
|
||||
}
|
||||
});
|
||||
|
||||
const lineColor = $derived($colors.edge.clone().convertSRGBToLinear());
|
||||
</script>
|
||||
|
||||
<T.Mesh
|
||||
@@ -110,6 +113,6 @@
|
||||
|
||||
{#if geometry}
|
||||
<T.Mesh position.x={from.x} position.z={from.y} position.y={0.1} {geometry}>
|
||||
<MeshLineMaterial width={3} attenuate={false} color={lineColor} />
|
||||
<MeshLineMaterial width={Math.max(z*0.0001,0.00001)} color={lineColor} />
|
||||
</T.Mesh>
|
||||
{/if}
|
||||
|
@@ -1,8 +1,12 @@
|
||||
<script lang="ts">
|
||||
import Edge from "./Edge.svelte";
|
||||
|
||||
type Props = { from: { x: number; y: number }; to: { x: number; y: number } };
|
||||
const { from, to }: Props = $props();
|
||||
type Props = {
|
||||
from: { x: number; y: number };
|
||||
to: { x: number; y: number };
|
||||
z: number;
|
||||
};
|
||||
const { from, to, z }: Props = $props();
|
||||
</script>
|
||||
|
||||
<Edge {from} {to} />
|
||||
<Edge {from} {to} {z} />
|
||||
|
@@ -1,11 +1,29 @@
|
||||
import { BufferGeometry, Vector3, BufferAttribute } from 'three'
|
||||
import { setXY, setXYZ, setXYZW, setXYZXYZ } from './utils.js'
|
||||
import { BufferAttribute, BufferGeometry, Vector3 } from 'three';
|
||||
import { setXY, setXYZ, setXYZW, setXYZXYZ } from './utils.js';
|
||||
|
||||
|
||||
export function createEdgeGeometry(points: Vector3[]) {
|
||||
|
||||
let shape = 'none'
|
||||
let shapeFunction = (p: number) => 1
|
||||
const length = points[0].distanceTo(points[points.length - 1]);
|
||||
|
||||
const startRadius = 10.5;
|
||||
const constantWidth = 2;
|
||||
const taperFraction = 0.8 / length;
|
||||
|
||||
function ease(t: number) {
|
||||
return t * t * (3 - 2 * t);
|
||||
}
|
||||
let shapeFunction = (alpha: number) => {
|
||||
if (alpha < taperFraction) {
|
||||
const easedAlpha = ease(alpha / taperFraction);
|
||||
return startRadius + (constantWidth - startRadius) * easedAlpha;
|
||||
} else if (alpha > 1 - taperFraction) {
|
||||
const easedAlpha = ease((alpha - (1 - taperFraction)) / taperFraction);
|
||||
return constantWidth + (startRadius - constantWidth) * easedAlpha;
|
||||
} else {
|
||||
return constantWidth;
|
||||
}
|
||||
};
|
||||
|
||||
// When the component first runs we create the buffer geometry and allocate the buffer attributes
|
||||
let pointCount = points.length
|
||||
@@ -19,9 +37,7 @@ export function createEdgeGeometry(points: Vector3[]) {
|
||||
let indices: number[] = []
|
||||
let indicesIndex = 0
|
||||
|
||||
if (shape === 'taper') {
|
||||
shapeFunction = (p: number) => 1 * Math.pow(4 * p * (1 - p), 1)
|
||||
}
|
||||
|
||||
|
||||
for (let j = 0; j < pointCount; j++) {
|
||||
const c = j / points.length
|
||||
@@ -30,7 +46,7 @@ export function createEdgeGeometry(points: Vector3[]) {
|
||||
counterIndex += 2
|
||||
|
||||
setXY(side, doubleIndex, 1, -1)
|
||||
let width = shape === 'none' ? 1 : shapeFunction(j / (pointCount - 1))
|
||||
let width = shapeFunction((j / (pointCount - 1)))
|
||||
setXY(widthArray, doubleIndex, width, width)
|
||||
doubleIndex += 2
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import type { Edge, Graph, Node, NodeInput, NodeRegistry, Socket, } from "@nodes/types";
|
||||
import { fastHashString } from "@nodes/utils";
|
||||
import { writable, type Writable } from "svelte/store";
|
||||
import { get, writable, type Writable } from "svelte/store";
|
||||
import EventEmitter from "./helpers/EventEmitter.js";
|
||||
import { createLogger } from "./helpers/index.js";
|
||||
import throttle from "./helpers/throttle.js";
|
||||
@@ -42,6 +42,7 @@ export class GraphManager extends EventEmitter<{ "save": Graph, "result": any, "
|
||||
|
||||
history: HistoryManager = new HistoryManager();
|
||||
execute = throttle(() => {
|
||||
console.log("Props", get(this.nodes).values().find(n => n.type === "max/plantarium/gravity")?.props);
|
||||
if (this.loaded === false) return;
|
||||
this.emit("result", this.serialize());
|
||||
}, 10);
|
||||
|
@@ -6,7 +6,6 @@
|
||||
} from "../helpers/index.js";
|
||||
import type { OrthographicCamera } from "three";
|
||||
import Background from "../background/Background.svelte";
|
||||
import type { GraphManager } from "../graph-manager.js";
|
||||
import { getContext, onMount, setContext } from "svelte";
|
||||
import Camera from "../Camera.svelte";
|
||||
import GraphView from "./GraphView.svelte";
|
||||
@@ -23,14 +22,13 @@
|
||||
import { Canvas } from "@threlte/core";
|
||||
import { getGraphManager } from "./context.js";
|
||||
|
||||
const state = getGraphState();
|
||||
const graphState = getGraphState();
|
||||
|
||||
export let snapToGrid = true;
|
||||
export let showGrid = true;
|
||||
export let showHelp = false;
|
||||
|
||||
let keymap =
|
||||
getContext<ReturnType<typeof createKeyMap>>("keymap") || createKeyMap([]);
|
||||
const keymap = getContext<ReturnType<typeof createKeyMap>>("keymap");
|
||||
|
||||
const manager = getGraphManager();
|
||||
|
||||
@@ -179,7 +177,7 @@
|
||||
}
|
||||
|
||||
setContext("setDownSocket", (socket: Socket) => {
|
||||
state.activeSocket = socket;
|
||||
graphState.activeSocket = socket;
|
||||
|
||||
let { node, index, position } = socket;
|
||||
|
||||
@@ -198,14 +196,14 @@
|
||||
}
|
||||
|
||||
mouseDown = position;
|
||||
state.activeSocket = {
|
||||
graphState.activeSocket = {
|
||||
node,
|
||||
index,
|
||||
position,
|
||||
};
|
||||
|
||||
state.possibleSockets = manager
|
||||
.getPossibleSockets(state.activeSocket)
|
||||
graphState.possibleSockets = manager
|
||||
.getPossibleSockets(graphState.activeSocket)
|
||||
.map(([node, index]) => {
|
||||
return {
|
||||
node,
|
||||
@@ -259,14 +257,15 @@
|
||||
let my = event.clientY - rect.y;
|
||||
|
||||
mousePosition = projectScreenToWorld(mx, my);
|
||||
hoveredNodeId = getNodeIdFromEvent(event);
|
||||
|
||||
if (!mouseDown) return;
|
||||
|
||||
// we are creating a new edge here
|
||||
if (state.activeSocket || state.possibleSockets?.length) {
|
||||
if (graphState.activeSocket || graphState.possibleSockets?.length) {
|
||||
let smallestDist = 1000;
|
||||
let _socket;
|
||||
for (const socket of state.possibleSockets) {
|
||||
for (const socket of graphState.possibleSockets) {
|
||||
const dist = Math.sqrt(
|
||||
(socket.position[0] - mousePosition[0]) ** 2 +
|
||||
(socket.position[1] - mousePosition[1]) ** 2,
|
||||
@@ -279,9 +278,9 @@
|
||||
|
||||
if (_socket && smallestDist < 0.9) {
|
||||
mousePosition = _socket.position;
|
||||
state.hoveredSocket = _socket;
|
||||
graphState.hoveredSocket = _socket;
|
||||
} else {
|
||||
state.hoveredSocket = null;
|
||||
graphState.hoveredSocket = null;
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -301,17 +300,17 @@
|
||||
const y = node.position[1];
|
||||
const height = getNodeHeight(node.type);
|
||||
if (x > x1 - 20 && x < x2 && y > y1 - height && y < y2) {
|
||||
state.selectedNodes?.add(node.id);
|
||||
graphState.selectedNodes?.add(node.id);
|
||||
} else {
|
||||
state.selectedNodes?.delete(node.id);
|
||||
graphState.selectedNodes?.delete(node.id);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// here we are handling dragging of nodes
|
||||
if (state.activeNodeId !== -1 && mouseDownId !== -1) {
|
||||
const node = manager.getNode(state.activeNodeId);
|
||||
if (graphState.activeNodeId !== -1 && mouseDownId !== -1) {
|
||||
const node = manager.getNode(graphState.activeNodeId);
|
||||
if (!node || event.buttons !== 1) return;
|
||||
|
||||
node.tmp = node.tmp || {};
|
||||
@@ -340,8 +339,8 @@
|
||||
const vecX = oldX - newX;
|
||||
const vecY = oldY - newY;
|
||||
|
||||
if (state.selectedNodes?.size) {
|
||||
for (const nodeId of state.selectedNodes) {
|
||||
if (graphState.selectedNodes?.size) {
|
||||
for (const nodeId of graphState.selectedNodes) {
|
||||
const n = manager.getNode(nodeId);
|
||||
if (!n?.tmp) continue;
|
||||
n.tmp.x = (n?.tmp?.downX || 0) - vecX;
|
||||
@@ -360,6 +359,7 @@
|
||||
}
|
||||
|
||||
// here we are handling panning of camera
|
||||
isPanning = true;
|
||||
let newX = cameraDown[0] - (mx - mouseDown[0]) / cameraPosition[2];
|
||||
let newY = cameraDown[1] - (my - mouseDown[1]) / cameraPosition[2];
|
||||
|
||||
@@ -424,43 +424,46 @@
|
||||
|
||||
// if we clicked on a node
|
||||
if (clickedNodeId !== -1) {
|
||||
if (state.activeNodeId === -1) {
|
||||
state.activeNodeId = clickedNodeId;
|
||||
if (graphState.activeNodeId === -1) {
|
||||
graphState.activeNodeId = clickedNodeId;
|
||||
// if the selected node is the same as the clicked node
|
||||
} else if (state.activeNodeId === clickedNodeId) {
|
||||
} else if (graphState.activeNodeId === clickedNodeId) {
|
||||
//$activeNodeId = -1;
|
||||
// if the clicked node is different from the selected node and secondary
|
||||
} else if (event.ctrlKey) {
|
||||
state.selectedNodes = state.selectedNodes || new Set();
|
||||
state.selectedNodes.add(state.activeNodeId);
|
||||
state.selectedNodes.delete(clickedNodeId);
|
||||
state.activeNodeId = clickedNodeId;
|
||||
graphState.selectedNodes.add(graphState.activeNodeId);
|
||||
graphState.selectedNodes.delete(clickedNodeId);
|
||||
graphState.activeNodeId = clickedNodeId;
|
||||
// select the node
|
||||
} else if (event.shiftKey) {
|
||||
const activeNode = manager.getNode(state.activeNodeId);
|
||||
const activeNode = manager.getNode(graphState.activeNodeId);
|
||||
const newNode = manager.getNode(clickedNodeId);
|
||||
if (activeNode && newNode) {
|
||||
const edge = manager.getNodesBetween(activeNode, newNode);
|
||||
if (edge) {
|
||||
const selected = new Set(edge.map((n) => n.id));
|
||||
selected.add(clickedNodeId);
|
||||
state.selectedNodes = selected;
|
||||
graphState.selectedNodes.clear();
|
||||
for (const node of edge) {
|
||||
graphState.selectedNodes.add(node.id);
|
||||
}
|
||||
graphState.selectedNodes.add(clickedNodeId);
|
||||
}
|
||||
}
|
||||
} else if (!state.selectedNodes?.has(clickedNodeId)) {
|
||||
state.activeNodeId = clickedNodeId;
|
||||
state.clearSelection();
|
||||
} else if (!graphState.selectedNodes.has(clickedNodeId)) {
|
||||
graphState.activeNodeId = clickedNodeId;
|
||||
graphState.clearSelection();
|
||||
}
|
||||
} else if (event.ctrlKey) {
|
||||
boxSelection = true;
|
||||
}
|
||||
const node = manager.getNode(state.activeNodeId);
|
||||
|
||||
const node = manager.getNode(graphState.activeNodeId);
|
||||
if (!node) return;
|
||||
node.tmp = node.tmp || {};
|
||||
node.tmp.downX = node.position[0];
|
||||
node.tmp.downY = node.position[1];
|
||||
if (state.selectedNodes) {
|
||||
for (const nodeId of state.selectedNodes) {
|
||||
|
||||
if (graphState.selectedNodes) {
|
||||
for (const nodeId of graphState.selectedNodes) {
|
||||
const n = manager.getNode(nodeId);
|
||||
if (!n) continue;
|
||||
n.tmp = n.tmp || {};
|
||||
@@ -471,8 +474,12 @@
|
||||
}
|
||||
|
||||
function copyNodes() {
|
||||
if (state.activeNodeId === -1 && !state.selectedNodes?.size) return;
|
||||
let _nodes = [state.activeNodeId, ...(state.selectedNodes?.values() || [])]
|
||||
if (graphState.activeNodeId === -1 && !graphState.selectedNodes?.size)
|
||||
return;
|
||||
let _nodes = [
|
||||
graphState.activeNodeId,
|
||||
...(graphState.selectedNodes?.values() || []),
|
||||
]
|
||||
.map((id) => manager.getNode(id))
|
||||
.filter(Boolean) as Node[];
|
||||
|
||||
@@ -508,7 +515,10 @@
|
||||
.filter(Boolean) as Node[];
|
||||
|
||||
const newNodes = manager.createGraph(_nodes, clipboard.edges);
|
||||
state.selectedNodes = new Set(newNodes.map((n) => n.id));
|
||||
graphState.selectedNodes.clear();
|
||||
for (const node of newNodes) {
|
||||
graphState.selectedNodes.add(node.id);
|
||||
}
|
||||
}
|
||||
|
||||
const isBodyFocused = () => document?.activeElement?.nodeName !== "INPUT";
|
||||
@@ -517,12 +527,14 @@
|
||||
key: "l",
|
||||
description: "Select linked nodes",
|
||||
callback: () => {
|
||||
const activeNode = manager.getNode(state.activeNodeId);
|
||||
const activeNode = manager.getNode(graphState.activeNodeId);
|
||||
if (activeNode) {
|
||||
const nodes = manager.getLinkedNodes(activeNode);
|
||||
state.selectedNodes = new Set(nodes.map((n) => n.id));
|
||||
graphState.selectedNodes.clear();
|
||||
for (const node of nodes) {
|
||||
graphState.selectedNodes.add(node.id);
|
||||
}
|
||||
}
|
||||
console.log(activeNode);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -552,8 +564,8 @@
|
||||
key: "Escape",
|
||||
description: "Deselect nodes",
|
||||
callback: () => {
|
||||
state.activeNodeId = -1;
|
||||
state.clearSelection();
|
||||
graphState.activeNodeId = -1;
|
||||
graphState.clearSelection();
|
||||
(document.activeElement as HTMLElement)?.blur();
|
||||
},
|
||||
});
|
||||
@@ -605,7 +617,9 @@
|
||||
description: "Select all nodes",
|
||||
callback: () => {
|
||||
if (!isBodyFocused()) return;
|
||||
state.selectedNodes = new Set($nodes.keys());
|
||||
for (const node of $nodes.keys()) {
|
||||
graphState.selectedNodes.add(node);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -654,38 +668,39 @@
|
||||
callback: (event) => {
|
||||
if (!isBodyFocused()) return;
|
||||
manager.startUndoGroup();
|
||||
if (state.activeNodeId !== -1) {
|
||||
const node = manager.getNode(state.activeNodeId);
|
||||
if (graphState.activeNodeId !== -1) {
|
||||
const node = manager.getNode(graphState.activeNodeId);
|
||||
if (node) {
|
||||
manager.removeNode(node, { restoreEdges: event.ctrlKey });
|
||||
state.activeNodeId = -1;
|
||||
graphState.activeNodeId = -1;
|
||||
}
|
||||
}
|
||||
if (state.selectedNodes) {
|
||||
for (const nodeId of state.selectedNodes) {
|
||||
if (graphState.selectedNodes) {
|
||||
for (const nodeId of graphState.selectedNodes) {
|
||||
const node = manager.getNode(nodeId);
|
||||
if (node) {
|
||||
manager.removeNode(node, { restoreEdges: event.ctrlKey });
|
||||
}
|
||||
}
|
||||
state.clearSelection();
|
||||
graphState.clearSelection();
|
||||
}
|
||||
manager.saveUndoGroup();
|
||||
},
|
||||
});
|
||||
|
||||
function handleMouseUp(event: MouseEvent) {
|
||||
isPanning = false;
|
||||
if (!mouseDown) return;
|
||||
|
||||
const activeNode = manager.getNode(state.activeNodeId);
|
||||
const activeNode = manager.getNode(graphState.activeNodeId);
|
||||
|
||||
const clickedNodeId = getNodeIdFromEvent(event);
|
||||
|
||||
if (clickedNodeId !== -1) {
|
||||
if (activeNode) {
|
||||
if (!activeNode?.tmp?.isMoving && !event.ctrlKey && !event.shiftKey) {
|
||||
state.clearSelection();
|
||||
state.activeNodeId = clickedNodeId;
|
||||
graphState.activeNodeId = clickedNodeId;
|
||||
graphState.clearSelection();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -708,7 +723,7 @@
|
||||
activeNode.position[1] = activeNode?.tmp?.y ?? activeNode.position[1];
|
||||
}
|
||||
const nodes = [
|
||||
...[...(state.selectedNodes?.values() || [])].map((id) =>
|
||||
...[...(graphState.selectedNodes?.values() || [])].map((id) =>
|
||||
manager.getNode(id),
|
||||
),
|
||||
] as NodeType[];
|
||||
@@ -747,26 +762,26 @@
|
||||
$edges = $edges;
|
||||
});
|
||||
manager.save();
|
||||
} else if (state.hoveredSocket && state.activeSocket) {
|
||||
} else if (graphState.hoveredSocket && graphState.activeSocket) {
|
||||
if (
|
||||
typeof state.hoveredSocket.index === "number" &&
|
||||
typeof state.activeSocket.index === "string"
|
||||
typeof graphState.hoveredSocket.index === "number" &&
|
||||
typeof graphState.activeSocket.index === "string"
|
||||
) {
|
||||
manager.createEdge(
|
||||
state.hoveredSocket.node,
|
||||
state.hoveredSocket.index || 0,
|
||||
state.activeSocket.node,
|
||||
state.activeSocket.index,
|
||||
graphState.hoveredSocket.node,
|
||||
graphState.hoveredSocket.index || 0,
|
||||
graphState.activeSocket.node,
|
||||
graphState.activeSocket.index,
|
||||
);
|
||||
} else if (
|
||||
typeof state.activeSocket.index == "number" &&
|
||||
typeof state.hoveredSocket.index === "string"
|
||||
typeof graphState.activeSocket.index == "number" &&
|
||||
typeof graphState.hoveredSocket.index === "string"
|
||||
) {
|
||||
manager.createEdge(
|
||||
state.activeSocket.node,
|
||||
state.activeSocket.index || 0,
|
||||
state.hoveredSocket.node,
|
||||
state.hoveredSocket.index,
|
||||
graphState.activeSocket.node,
|
||||
graphState.activeSocket.index || 0,
|
||||
graphState.hoveredSocket.node,
|
||||
graphState.hoveredSocket.index,
|
||||
);
|
||||
}
|
||||
manager.save();
|
||||
@@ -780,22 +795,25 @@
|
||||
cameraDown[1] === cameraPosition[1] &&
|
||||
isBodyFocused()
|
||||
) {
|
||||
state.activeNodeId = -1;
|
||||
state.clearSelection();
|
||||
graphState.activeNodeId = -1;
|
||||
graphState.clearSelection();
|
||||
}
|
||||
|
||||
mouseDown = null;
|
||||
boxSelection = false;
|
||||
state.activeSocket = null;
|
||||
state.possibleSockets = [];
|
||||
state.hoveredSocket = null;
|
||||
graphState.activeSocket = null;
|
||||
graphState.possibleSockets = [];
|
||||
graphState.hoveredSocket = null;
|
||||
addMenuPosition = null;
|
||||
}
|
||||
|
||||
let isPanning = false;
|
||||
let isDragging = false;
|
||||
let hoveredNodeId = -1;
|
||||
|
||||
function handleMouseLeave() {
|
||||
isDragging = false;
|
||||
isPanning = false;
|
||||
}
|
||||
|
||||
function handleDrop(event: DragEvent) {
|
||||
@@ -865,16 +883,19 @@
|
||||
function handleDragEnter(e: DragEvent) {
|
||||
e.preventDefault();
|
||||
isDragging = true;
|
||||
isPanning = false;
|
||||
}
|
||||
|
||||
function handlerDragOver(e: DragEvent) {
|
||||
isDragging = true;
|
||||
e.preventDefault();
|
||||
isDragging = true;
|
||||
isPanning = false;
|
||||
}
|
||||
|
||||
function handleDragEnd(e: DragEvent) {
|
||||
isDragging = false;
|
||||
e.preventDefault();
|
||||
isDragging = true;
|
||||
isPanning = false;
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
@@ -893,6 +914,8 @@
|
||||
on:wheel={handleMouseScroll}
|
||||
bind:this={wrapper}
|
||||
class="graph-wrapper"
|
||||
class:is-panning={isPanning}
|
||||
class:is-hovering={hoveredNodeId !== -1}
|
||||
aria-label="Graph"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
@@ -916,9 +939,6 @@
|
||||
/>
|
||||
<label for="drop-zone"></label>
|
||||
|
||||
{#if showHelp}
|
||||
<HelpView registry={manager.registry} />
|
||||
{/if}
|
||||
<Canvas shadows={false} renderMode="on-demand" colorManagementEnabled={false}>
|
||||
<Camera bind:camera position={cameraPosition} />
|
||||
|
||||
@@ -943,11 +963,12 @@
|
||||
<AddMenu bind:position={addMenuPosition} graph={manager} />
|
||||
{/if}
|
||||
|
||||
{#if state.activeSocket}
|
||||
{#if graphState.activeSocket}
|
||||
<FloatingEdge
|
||||
z={cameraPosition[2]}
|
||||
from={{
|
||||
x: state.activeSocket.position[0],
|
||||
y: state.activeSocket.position[1],
|
||||
x: graphState.activeSocket.position[0],
|
||||
y: graphState.activeSocket.position[1],
|
||||
}}
|
||||
to={{ x: mousePosition[0], y: mousePosition[1] }}
|
||||
/>
|
||||
@@ -962,6 +983,10 @@
|
||||
</Canvas>
|
||||
</div>
|
||||
|
||||
{#if showHelp}
|
||||
<HelpView registry={manager.registry} />
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
.graph-wrapper {
|
||||
position: relative;
|
||||
@@ -969,6 +994,15 @@
|
||||
transition: opacity 0.3s ease;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.is-hovering {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.is-panning {
|
||||
cursor: grab;
|
||||
}
|
||||
|
||||
input {
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
|
@@ -6,10 +6,23 @@
|
||||
import { getContext, onMount } from "svelte";
|
||||
import type { Writable } from "svelte/store";
|
||||
import { getGraphState } from "./state.svelte";
|
||||
import { useThrelte } from "@threlte/core";
|
||||
import { appSettings } from "$lib/settings/app-settings.svelte";
|
||||
|
||||
export let nodes: Writable<Map<number, NodeType>>;
|
||||
export let edges: Writable<EdgeType[]>;
|
||||
export let cameraPosition = [0, 0, 4];
|
||||
type Props = {
|
||||
nodes: Writable<Map<number, NodeType>>;
|
||||
edges: Writable<EdgeType[]>;
|
||||
cameraPosition: [number, number, number];
|
||||
};
|
||||
|
||||
const { nodes, edges, cameraPosition = [0, 0, 4] }: Props = $props();
|
||||
|
||||
const { invalidate } = useThrelte();
|
||||
|
||||
$effect(() => {
|
||||
appSettings.theme;
|
||||
invalidate();
|
||||
});
|
||||
|
||||
const graphState = getGraphState();
|
||||
|
||||
@@ -23,7 +36,6 @@
|
||||
function getEdgePosition(edge: EdgeType) {
|
||||
const pos1 = getSocketPosition(edge[0], edge[1]);
|
||||
const pos2 = getSocketPosition(edge[2], edge[3]);
|
||||
|
||||
return [pos1[0], pos1[1], pos2[0], pos2[1]];
|
||||
}
|
||||
|
||||
@@ -41,6 +53,7 @@
|
||||
{@const pos = getEdgePosition(edge)}
|
||||
{@const [x1, y1, x2, y2] = pos}
|
||||
<Edge
|
||||
z={cameraPosition[2]}
|
||||
from={{
|
||||
x: x1,
|
||||
y: y1,
|
||||
|
@@ -3,19 +3,18 @@
|
||||
import GraphEl from "./Graph.svelte";
|
||||
import { GraphManager } from "../graph-manager.js";
|
||||
import { setContext } from "svelte";
|
||||
import { type Writable } from "svelte/store";
|
||||
import { debounce } from "$lib/helpers";
|
||||
import { createKeyMap } from "$lib/helpers/createKeyMap";
|
||||
import { GraphState } from "./state.svelte";
|
||||
|
||||
const state = new GraphState();
|
||||
setContext("graphState", state);
|
||||
const graphState = new GraphState();
|
||||
setContext("graphState", graphState);
|
||||
|
||||
type Props = {
|
||||
graph: Graph;
|
||||
registry: NodeRegistry;
|
||||
|
||||
settings?: Writable<Record<string, any>>;
|
||||
settings?: Record<string, any>;
|
||||
|
||||
activeNode?: Node;
|
||||
showGrid?: boolean;
|
||||
@@ -41,33 +40,32 @@
|
||||
}: Props = $props();
|
||||
|
||||
export const keymap = createKeyMap([]);
|
||||
setContext("keymap", keymap);
|
||||
|
||||
export const manager = new GraphManager(registry);
|
||||
setContext("graphManager", manager);
|
||||
|
||||
$effect(() => {
|
||||
if (state.activeNodeId !== -1) {
|
||||
activeNode = manager.getNode(state.activeNodeId);
|
||||
if (graphState.activeNodeId !== -1) {
|
||||
activeNode = manager.getNode(graphState.activeNodeId);
|
||||
} else {
|
||||
activeNode = undefined;
|
||||
}
|
||||
});
|
||||
|
||||
setContext("keymap", keymap);
|
||||
|
||||
const updateSettings = debounce((s) => {
|
||||
manager.setSettings(s);
|
||||
}, 200);
|
||||
|
||||
$effect(() => {
|
||||
if (settingTypes && settings) {
|
||||
updateSettings($settings);
|
||||
updateSettings($state.snapshot(settings));
|
||||
}
|
||||
});
|
||||
|
||||
manager.on("settings", (_settings) => {
|
||||
settingTypes = _settings.types;
|
||||
settings?.set(_settings.values);
|
||||
settings = _settings.values;
|
||||
});
|
||||
|
||||
manager.on("result", (result) => onresult?.(result));
|
||||
|
31
app/src/lib/graph-interface/graph/colors.svelte.ts
Normal file
31
app/src/lib/graph-interface/graph/colors.svelte.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import { appSettings } from "$lib/settings/app-settings.svelte";
|
||||
import { Color } from "three";
|
||||
|
||||
const variables = [
|
||||
"layer-0",
|
||||
"layer-1",
|
||||
"layer-2",
|
||||
"layer-3",
|
||||
"outline",
|
||||
"active",
|
||||
"selected",
|
||||
"edge",
|
||||
] as const;
|
||||
|
||||
function getColor(variable: typeof variables[number]) {
|
||||
const style = getComputedStyle(document.body.parentElement!);
|
||||
let color = style.getPropertyValue(`--${variable}`);
|
||||
return new Color().setStyle(color);
|
||||
}
|
||||
|
||||
export const colors = Object.fromEntries(variables.map(v => [v, getColor(v)])) as Record<typeof variables[number], Color>;
|
||||
|
||||
$effect.root(() => {
|
||||
$effect(() => {
|
||||
if (!appSettings.theme || !("getComputedStyle" in globalThis)) return;
|
||||
const style = getComputedStyle(document.body.parentElement!);
|
||||
for (const v of variables) {
|
||||
colors[v].setStyle(style.getPropertyValue(`--${v}`));
|
||||
}
|
||||
});
|
||||
})
|
@@ -1,48 +0,0 @@
|
||||
import { readable } from "svelte/store";
|
||||
import { Color } from "three";
|
||||
|
||||
const variables = [
|
||||
"layer-0",
|
||||
"layer-1",
|
||||
"layer-2",
|
||||
"layer-3",
|
||||
"outline",
|
||||
"active",
|
||||
"selected",
|
||||
"edge",
|
||||
] as const;
|
||||
|
||||
const store = Object.fromEntries(variables.map(v => [v, new Color()])) as Record<typeof variables[number], Color>;
|
||||
|
||||
let lastStyle = "";
|
||||
|
||||
function updateColors() {
|
||||
if (!("getComputedStyle" in globalThis)) return;
|
||||
const style = getComputedStyle(document.body.parentElement!);
|
||||
let hash = "";
|
||||
for (const v of variables) {
|
||||
let color = style.getPropertyValue(`--${v}`);
|
||||
hash += color;
|
||||
store[v].setStyle(color);
|
||||
}
|
||||
if (hash === lastStyle) return;
|
||||
lastStyle = hash;
|
||||
}
|
||||
|
||||
export const colors = readable(store, set => {
|
||||
|
||||
updateColors();
|
||||
set(store);
|
||||
|
||||
setTimeout(() => {
|
||||
updateColors();
|
||||
set(store);
|
||||
}, 1000);
|
||||
|
||||
window.onload = function () { updateColors(); set(store) };
|
||||
|
||||
document.body.addEventListener("transitionstart", () => {
|
||||
updateColors();
|
||||
set(store);
|
||||
})
|
||||
});
|
@@ -1,26 +1,22 @@
|
||||
import type { Socket } from "@nodes/types";
|
||||
import { getContext } from "svelte";
|
||||
import { SvelteSet } from 'svelte/reactivity';
|
||||
|
||||
export function getGraphState() {
|
||||
return getContext<GraphState>("graphState");
|
||||
}
|
||||
|
||||
export class GraphState {
|
||||
|
||||
activeNodeId = $state(-1);
|
||||
selectedNodes = $state(new Set<number>());
|
||||
selectedNodes = new SvelteSet<number>();
|
||||
activeSocket = $state<Socket | null>(null);
|
||||
hoveredSocket = $state<Socket | null>(null);
|
||||
possibleSockets = $state<Socket[]>([]);
|
||||
possibleSocketIds = $derived(new Set(
|
||||
this.possibleSockets.map((s) => `${s.node.id}-${s.index}`),
|
||||
));
|
||||
|
||||
clearSelection() {
|
||||
this.selectedNodes = new Set();
|
||||
this.selectedNodes.clear();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { colors } from "./colors";
|
||||
|
||||
|
@@ -1,12 +1,14 @@
|
||||
<script lang="ts">
|
||||
import type { Node } from "@nodes/types";
|
||||
import { getContext, onMount } from "svelte";
|
||||
import { colors, getGraphState } from "../graph/state.svelte";
|
||||
import { getGraphState } from "../graph/state.svelte";
|
||||
import { T } from "@threlte/core";
|
||||
import { Color, type Mesh } from "three";
|
||||
import { type Mesh } from "three";
|
||||
import NodeFrag from "./Node.frag";
|
||||
import NodeVert from "./Node.vert";
|
||||
import NodeHtml from "./NodeHTML.svelte";
|
||||
import { colors } from "../graph/colors.svelte";
|
||||
import { appSettings } from "$lib/settings/app-settings.svelte";
|
||||
|
||||
const graphState = getGraphState();
|
||||
|
||||
@@ -18,7 +20,16 @@
|
||||
const { node, inView, z }: Props = $props();
|
||||
|
||||
const isActive = $derived(graphState.activeNodeId === node.id);
|
||||
const isSelected = $derived(!!graphState.selectedNodes?.has(node.id));
|
||||
const isSelected = $derived(graphState.selectedNodes.has(node.id));
|
||||
let strokeColor = $state(colors.selected);
|
||||
$effect(() => {
|
||||
appSettings.theme;
|
||||
strokeColor = isSelected
|
||||
? colors.selected.clone()
|
||||
: isActive
|
||||
? colors.active.clone()
|
||||
: colors.outline.clone();
|
||||
});
|
||||
|
||||
const updateNodePosition =
|
||||
getContext<(n: Node) => void>("updateNodePosition");
|
||||
@@ -30,16 +41,11 @@
|
||||
const height = getNodeHeight?.(node.type);
|
||||
|
||||
$effect(() => {
|
||||
if (node && meshRef) {
|
||||
node.tmp = node.tmp || {};
|
||||
node.tmp.mesh = meshRef;
|
||||
updateNodePosition?.(node);
|
||||
}
|
||||
node.tmp = node.tmp || {};
|
||||
node.tmp.mesh = meshRef;
|
||||
updateNodePosition?.(node);
|
||||
});
|
||||
|
||||
const colorBright = $colors["layer-2"];
|
||||
const colorDark = $colors["layer-1"];
|
||||
|
||||
onMount(() => {
|
||||
node.tmp = node.tmp || {};
|
||||
node.tmp.mesh = meshRef;
|
||||
@@ -61,20 +67,14 @@
|
||||
fragmentShader={NodeFrag}
|
||||
transparent
|
||||
uniforms={{
|
||||
uColorBright: { value: new Color("#171717") },
|
||||
uColorDark: { value: new Color("#151515") },
|
||||
uStrokeColor: { value: new Color("#9d5f28") },
|
||||
uColorBright: { value: colors["layer-2"] },
|
||||
uColorDark: { value: colors["layer-1"] },
|
||||
uStrokeColor: { value: colors.outline.clone() },
|
||||
uStrokeWidth: { value: 1.0 },
|
||||
uWidth: { value: 20 },
|
||||
uHeight: { value: height },
|
||||
}}
|
||||
uniforms.uColorBright.value={colorBright}
|
||||
uniforms.uColorDark.value={colorDark}
|
||||
uniforms.uStrokeColor.value={isSelected
|
||||
? $colors.selected
|
||||
: isActive
|
||||
? $colors.active
|
||||
: $colors.outline}
|
||||
uniforms.uStrokeColor.value={strokeColor.clone()}
|
||||
uniforms.uStrokeWidth.value={(7 - z) / 3}
|
||||
/>
|
||||
</T.Mesh>
|
||||
|
@@ -3,14 +3,27 @@
|
||||
import NodeHeader from "./NodeHeader.svelte";
|
||||
import NodeParameter from "./NodeParameter.svelte";
|
||||
import { getContext, onMount } from "svelte";
|
||||
export let isActive = false;
|
||||
export let isSelected = false;
|
||||
export let inView = true;
|
||||
export let z = 2;
|
||||
|
||||
let ref: HTMLDivElement;
|
||||
export let node: Node;
|
||||
export let position = "absolute";
|
||||
|
||||
type Props = {
|
||||
node: Node;
|
||||
|
||||
position?: "absolute" | "fixed";
|
||||
isActive?: boolean;
|
||||
isSelected?: boolean;
|
||||
inView?: boolean;
|
||||
z?: number;
|
||||
};
|
||||
|
||||
let {
|
||||
node,
|
||||
position = "absolute",
|
||||
isActive = false,
|
||||
isSelected = false,
|
||||
inView = true,
|
||||
z = 2,
|
||||
}: Props = $props();
|
||||
|
||||
const zOffset = (node.tmp?.random || 0) * 0.5;
|
||||
const zLimit = 2 - zOffset;
|
||||
@@ -25,12 +38,6 @@
|
||||
const updateNodePosition =
|
||||
getContext<(n: Node) => void>("updateNodePosition");
|
||||
|
||||
$: if (node && ref) {
|
||||
node.tmp = node.tmp || {};
|
||||
node.tmp.ref = ref;
|
||||
updateNodePosition?.(node);
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
node.tmp = node.tmp || {};
|
||||
node.tmp.ref = ref;
|
||||
|
@@ -26,9 +26,9 @@
|
||||
const aspectRatio = 0.25;
|
||||
|
||||
const path = createNodePath({
|
||||
depth: 5,
|
||||
height: 29,
|
||||
y: 50,
|
||||
depth: 5.5,
|
||||
height: 34,
|
||||
y: 49,
|
||||
cornerTop,
|
||||
rightBump,
|
||||
aspectRatio,
|
||||
@@ -42,9 +42,9 @@
|
||||
aspectRatio,
|
||||
});
|
||||
const pathHover = createNodePath({
|
||||
depth: 9,
|
||||
depth: 8.5,
|
||||
height: 50,
|
||||
y: 50,
|
||||
y: 49,
|
||||
cornerTop,
|
||||
rightBump,
|
||||
aspectRatio,
|
||||
@@ -103,12 +103,12 @@
|
||||
|
||||
svg {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
top: 1px;
|
||||
left: 1px;
|
||||
z-index: -1;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
width: calc(100% - 2px);
|
||||
height: calc(100% - 1px);
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
|
@@ -53,14 +53,14 @@
|
||||
const path = createNodePath({
|
||||
depth: 7,
|
||||
height: 20,
|
||||
y: 51,
|
||||
y: 50.5,
|
||||
cornerBottom,
|
||||
leftBump,
|
||||
aspectRatio,
|
||||
});
|
||||
const pathDisabled = createNodePath({
|
||||
depth: 4.5,
|
||||
height: 14,
|
||||
depth: 6,
|
||||
height: 18,
|
||||
y: 50.5,
|
||||
cornerBottom,
|
||||
leftBump,
|
||||
@@ -172,11 +172,11 @@
|
||||
svg {
|
||||
position: absolute;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
width: calc(100% - 2px);
|
||||
height: 100%;
|
||||
overflow: visible;
|
||||
top: 0;
|
||||
left: 0;
|
||||
left: 1px;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
|
@@ -1,20 +0,0 @@
|
||||
import type { Node, NodeDefinition } from "@nodes/types";
|
||||
|
||||
export type GraphNode = Node & {
|
||||
tmp?: {
|
||||
depth?: number;
|
||||
mesh?: any;
|
||||
random?: number;
|
||||
parents?: Node[];
|
||||
children?: Node[];
|
||||
inputNodes?: Record<string, Node>;
|
||||
type?: NodeDefinition;
|
||||
downX?: number;
|
||||
downY?: number;
|
||||
x?: number;
|
||||
y?: number;
|
||||
ref?: HTMLElement;
|
||||
visible?: boolean;
|
||||
isMoving?: boolean;
|
||||
};
|
||||
};
|
@@ -18,7 +18,6 @@ export function createKeyMap(keys: Shortcut[]) {
|
||||
|
||||
const store = writable(new Map(keys.map(k => [getShortcutId(k), k])));
|
||||
|
||||
|
||||
return {
|
||||
handleKeyboardEvent: (event: KeyboardEvent) => {
|
||||
const activeElement = document.activeElement as HTMLElement;
|
||||
|
@@ -65,7 +65,7 @@ export function createNodePath({
|
||||
|
||||
export const debounce = (fn: Function, ms = 300) => {
|
||||
let timeoutId: ReturnType<typeof setTimeout>;
|
||||
return function (this: any, ...args: any[]) {
|
||||
return function(this: any, ...args: any[]) {
|
||||
clearTimeout(timeoutId);
|
||||
timeoutId = setTimeout(() => fn.apply(this, args), ms);
|
||||
};
|
||||
@@ -131,99 +131,40 @@ export function humanizeDuration(durationInMilliseconds: number) {
|
||||
|
||||
return durationString.trim();
|
||||
}
|
||||
// export function debounceAsyncFunction<T extends any[], R>(
|
||||
// func: (...args: T) => Promise<R>
|
||||
// ): (...args: T) => Promise<R> {
|
||||
// let timeoutId: ReturnType<typeof setTimeout> | null = null;
|
||||
// let lastPromise: Promise<R> | null = null;
|
||||
// let lastReject: ((reason?: any) => void) | null = null;
|
||||
//
|
||||
// return (...args: T): Promise<R> => {
|
||||
// if (timeoutId) {
|
||||
// clearTimeout(timeoutId);
|
||||
// if (lastReject) {
|
||||
// lastReject(new Error("Debounced: Previous call was canceled."));
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// return new Promise<R>((resolve, reject) => {
|
||||
// lastReject = reject;
|
||||
// timeoutId = setTimeout(() => {
|
||||
// timeoutId = null;
|
||||
// lastReject = null;
|
||||
// lastPromise = func(...args).then(resolve, reject);
|
||||
// }, 300); // Default debounce time is 300ms; you can make this configurable.
|
||||
// });
|
||||
// };
|
||||
// }
|
||||
export function debounceAsyncFunction<T extends (...args: any[]) => Promise<any>>(asyncFn: T): T {
|
||||
let isRunning = false;
|
||||
let latestArgs: Parameters<T> | null = null;
|
||||
let resolveNext: (() => void) | null = null;
|
||||
export function debounceAsyncFunction<T extends any[], R>(func: (...args: T) => Promise<R>): (...args: T) => Promise<R> {
|
||||
let currentPromise: Promise<R> | null = null;
|
||||
let nextArgs: T | null = null;
|
||||
let resolveNext: ((result: R) => void) | null = null;
|
||||
|
||||
return (async function serializedFunction(...args: Parameters<T>): Promise<ReturnType<T>> {
|
||||
latestArgs = args;
|
||||
|
||||
if (isRunning) {
|
||||
// Wait for the current execution to finish
|
||||
await new Promise<void>((resolve) => {
|
||||
const debouncedFunction = async (...args: T): Promise<R> => {
|
||||
if (currentPromise) {
|
||||
// Store the latest arguments and create a new promise to resolve them later
|
||||
nextArgs = args;
|
||||
return new Promise<R>((resolve) => {
|
||||
resolveNext = resolve;
|
||||
});
|
||||
}
|
||||
|
||||
// Indicate the function is running
|
||||
isRunning = true;
|
||||
|
||||
try {
|
||||
// Execute with the latest arguments
|
||||
const result = await asyncFn(...latestArgs!);
|
||||
return result;
|
||||
} finally {
|
||||
// Allow the next execution
|
||||
isRunning = false;
|
||||
|
||||
if (resolveNext) {
|
||||
resolveNext();
|
||||
resolveNext = null;
|
||||
} else {
|
||||
// Execute the function immediately
|
||||
try {
|
||||
currentPromise = func(...args);
|
||||
const result = await currentPromise;
|
||||
return result;
|
||||
} finally {
|
||||
currentPromise = null;
|
||||
// If there are stored arguments, call the function again with the latest arguments
|
||||
if (nextArgs) {
|
||||
const argsToUse = nextArgs;
|
||||
const resolver = resolveNext;
|
||||
nextArgs = null;
|
||||
resolveNext = null;
|
||||
resolver!(await debouncedFunction(...argsToUse));
|
||||
}
|
||||
}
|
||||
}
|
||||
}) as T;
|
||||
}
|
||||
};
|
||||
|
||||
// export function debounceAsyncFunction<T extends any[], R>(func: (...args: T) => Promise<R>): (...args: T) => Promise<R> {
|
||||
// let currentPromise: Promise<R> | null = null;
|
||||
// let nextArgs: T | null = null;
|
||||
// let resolveNext: ((result: R) => void) | null = null;
|
||||
//
|
||||
// const debouncedFunction = async (...args: T): Promise<R> => {
|
||||
// if (currentPromise) {
|
||||
// // Store the latest arguments and create a new promise to resolve them later
|
||||
// nextArgs = args;
|
||||
// return new Promise<R>((resolve) => {
|
||||
// resolveNext = resolve;
|
||||
// });
|
||||
// } else {
|
||||
// // Execute the function immediately
|
||||
// try {
|
||||
// currentPromise = func(...args);
|
||||
// const result = await currentPromise;
|
||||
// return result;
|
||||
// } finally {
|
||||
// currentPromise = null;
|
||||
// // If there are stored arguments, call the function again with the latest arguments
|
||||
// if (nextArgs) {
|
||||
// const argsToUse = nextArgs;
|
||||
// const resolver = resolveNext;
|
||||
// nextArgs = null;
|
||||
// resolveNext = null;
|
||||
// resolver!(await debouncedFunction(...argsToUse));
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// };
|
||||
//
|
||||
// return debouncedFunction;
|
||||
// }
|
||||
return debouncedFunction;
|
||||
}
|
||||
|
||||
export function withArgsChangeOnly<T extends any[], R>(func: (...args: T) => R): (...args: T) => R {
|
||||
let lastArgs: T | undefined = undefined;
|
||||
|
@@ -3,6 +3,7 @@
|
||||
import type { NodeDefinition } from "@nodes/types";
|
||||
|
||||
export let node: NodeDefinition;
|
||||
console.log(node);
|
||||
|
||||
let dragging = false;
|
||||
|
||||
|
@@ -11,9 +11,9 @@
|
||||
fps: false,
|
||||
});
|
||||
|
||||
$: vertices = $store?.at(-1)?.["total-vertices"][0] || 0;
|
||||
$: faces = $store?.at(-1)?.["total-faces"][0] || 0;
|
||||
$: runtime = $store?.at(-1)?.["runtime"][0] || 0;
|
||||
$: vertices = $store?.at(-1)?.["total-vertices"]?.[0] || 0;
|
||||
$: faces = $store?.at(-1)?.["total-faces"]?.[0] || 0;
|
||||
$: runtime = $store?.at(-1)?.["runtime"]?.[0] || 0;
|
||||
|
||||
function getPoints(data: PerformanceData, key: string) {
|
||||
return data?.map((run) => run[key]?.[0] || 0) || [];
|
||||
|
@@ -9,21 +9,28 @@
|
||||
Box3,
|
||||
Mesh,
|
||||
MeshBasicMaterial,
|
||||
Color,
|
||||
} from "three";
|
||||
import { AppSettings } from "../settings/app-settings";
|
||||
import { appSettings } from "../settings/app-settings.svelte";
|
||||
import Camera from "./Camera.svelte";
|
||||
import { colors } from "$lib/graph-interface/graph/colors.svelte";
|
||||
|
||||
const { renderStage, invalidate: _invalidate } = useThrelte();
|
||||
|
||||
export let fps: number[] = [];
|
||||
// let renderer = threlte.renderer;
|
||||
// let rendererRender = renderer.render;
|
||||
// renderer.render = function (scene, camera) {
|
||||
// const a = performance.now();
|
||||
// rendererRender.call(renderer, scene, camera);
|
||||
// fps.push(performance.now() - a);
|
||||
// fps = fps.slice(-100);
|
||||
// };
|
||||
type Props = {
|
||||
fps: number[];
|
||||
lines: Vector3[][];
|
||||
scene: Group;
|
||||
centerCamera: boolean;
|
||||
};
|
||||
|
||||
let {
|
||||
lines,
|
||||
centerCamera,
|
||||
fps = $bindable(),
|
||||
scene = $bindable(),
|
||||
}: Props = $props();
|
||||
|
||||
useTask(
|
||||
(delta) => {
|
||||
fps.push(1 / delta);
|
||||
@@ -53,12 +60,8 @@
|
||||
_invalidate();
|
||||
};
|
||||
|
||||
let geometries: BufferGeometry[] = [];
|
||||
export let lines: Vector3[][];
|
||||
export let scene: Group;
|
||||
|
||||
export let centerCamera: boolean = true;
|
||||
let center = new Vector3(0, 4, 0);
|
||||
let geometries = $state<BufferGeometry[]>();
|
||||
let center = $state(new Vector3(0, 4, 0));
|
||||
|
||||
function isMesh(child: Mesh | any): child is Mesh {
|
||||
return child.isObject3D && "material" in child;
|
||||
@@ -68,14 +71,15 @@
|
||||
return material.isMaterial && "matcap" in material;
|
||||
}
|
||||
|
||||
$: if ($AppSettings && scene) {
|
||||
$effect(() => {
|
||||
const wireframe = appSettings.debug.wireframe;
|
||||
scene.traverse(function (child) {
|
||||
if (isMesh(child) && isMatCapMaterial(child.material)) {
|
||||
child.material.wireframe = $AppSettings.wireframe;
|
||||
child.material.wireframe = wireframe;
|
||||
}
|
||||
});
|
||||
invalidate();
|
||||
}
|
||||
_invalidate();
|
||||
});
|
||||
|
||||
function getPosition(geo: BufferGeometry, i: number) {
|
||||
return [
|
||||
@@ -88,32 +92,38 @@
|
||||
|
||||
<Camera {center} {centerCamera} />
|
||||
|
||||
{#if $AppSettings.showGrid}
|
||||
<T.GridHelper args={[20, 20]} />
|
||||
{#if appSettings.showGrid}
|
||||
<T.GridHelper
|
||||
args={[20, 20]}
|
||||
colorGrid={colors["outline"]}
|
||||
colorCenterLine={new Color("red")}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
<T.Group>
|
||||
{#each geometries as geo}
|
||||
{#if $AppSettings.showIndices}
|
||||
{#each geo.attributes.position.array as _, i}
|
||||
{#if i % 3 === 0}
|
||||
<Text fontSize={0.25} position={getPosition(geo, i)} />
|
||||
{/if}
|
||||
{/each}
|
||||
{/if}
|
||||
{#if geometries}
|
||||
{#each geometries as geo}
|
||||
{#if appSettings.debug.showIndices}
|
||||
{#each geo.attributes.position.array as _, i}
|
||||
{#if i % 3 === 0}
|
||||
<Text fontSize={0.25} position={getPosition(geo, i)} />
|
||||
{/if}
|
||||
{/each}
|
||||
{/if}
|
||||
|
||||
{#if $AppSettings.showVertices}
|
||||
<T.Points visible={true}>
|
||||
<T is={geo} />
|
||||
<T.PointsMaterial size={0.25} />
|
||||
</T.Points>
|
||||
{/if}
|
||||
{/each}
|
||||
{#if appSettings.debug.showVertices}
|
||||
<T.Points visible={true}>
|
||||
<T is={geo} />
|
||||
<T.PointsMaterial size={0.25} />
|
||||
</T.Points>
|
||||
{/if}
|
||||
{/each}
|
||||
{/if}
|
||||
|
||||
<T.Group bind:ref={scene}></T.Group>
|
||||
</T.Group>
|
||||
|
||||
{#if $AppSettings.showStemLines && lines}
|
||||
{#if appSettings.debug.showStemLines && lines}
|
||||
{#each lines as line}
|
||||
<T.Mesh>
|
||||
<MeshLineGeometry points={line} />
|
||||
|
@@ -2,12 +2,10 @@
|
||||
import { Canvas } from "@threlte/core";
|
||||
import Scene from "./Scene.svelte";
|
||||
import { Vector3 } from "three";
|
||||
|
||||
import { decodeFloat, splitNestedArray } from "@nodes/utils";
|
||||
import type { PerformanceStore } from "@nodes/utils";
|
||||
import { AppSettings } from "$lib/settings/app-settings";
|
||||
import { appSettings } from "$lib/settings/app-settings.svelte";
|
||||
import SmallPerformanceViewer from "$lib/performance/SmallPerformanceViewer.svelte";
|
||||
|
||||
import { MeshMatcapMaterial, TextureLoader, type Group } from "three";
|
||||
import {
|
||||
createGeometryPool,
|
||||
@@ -22,9 +20,11 @@
|
||||
matcap,
|
||||
});
|
||||
|
||||
let sceneComponent = $state<ReturnType<typeof Scene>>();
|
||||
let fps = $state<number[]>([]);
|
||||
|
||||
let geometryPool: ReturnType<typeof createGeometryPool>;
|
||||
let instancePool: ReturnType<typeof createInstancedGeometryPool>;
|
||||
|
||||
export function updateGeometries(inputs: Int32Array[], group: Group) {
|
||||
geometryPool = geometryPool || createGeometryPool(group, material);
|
||||
instancePool = instancePool || createInstancedGeometryPool(group, material);
|
||||
@@ -38,14 +38,15 @@
|
||||
};
|
||||
}
|
||||
|
||||
export let centerCamera: boolean = true;
|
||||
export let perf: PerformanceStore;
|
||||
export let scene: Group;
|
||||
let fps: number[] = [];
|
||||
type Props = {
|
||||
scene: Group;
|
||||
centerCamera: boolean;
|
||||
perf: PerformanceStore;
|
||||
};
|
||||
|
||||
let lines: Vector3[][] = [];
|
||||
let { scene = $bindable(), centerCamera, perf }: Props = $props();
|
||||
|
||||
let invalidate: () => void;
|
||||
let lines = $state<Vector3[][]>([]);
|
||||
|
||||
function createLineGeometryFromEncodedData(encodedData: Int32Array) {
|
||||
const positions: Vector3[] = [];
|
||||
@@ -63,12 +64,12 @@
|
||||
}
|
||||
|
||||
export const update = function update(result: Int32Array) {
|
||||
perf?.addPoint("split-result");
|
||||
perf.addPoint("split-result");
|
||||
const inputs = splitNestedArray(result);
|
||||
perf?.endPoint();
|
||||
perf.endPoint();
|
||||
|
||||
if ($AppSettings.showStemLines) {
|
||||
perf?.addPoint("create-lines");
|
||||
if (appSettings.debug.showStemLines) {
|
||||
perf.addPoint("create-lines");
|
||||
lines = inputs
|
||||
.map((input) => {
|
||||
if (input[0] === 0) {
|
||||
@@ -79,21 +80,27 @@
|
||||
perf.endPoint();
|
||||
}
|
||||
|
||||
perf?.addPoint("update-geometries");
|
||||
perf.addPoint("update-geometries");
|
||||
|
||||
const { totalVertices, totalFaces } = updateGeometries(inputs, scene);
|
||||
perf?.endPoint();
|
||||
perf.endPoint();
|
||||
|
||||
perf?.addPoint("total-vertices", totalVertices);
|
||||
perf?.addPoint("total-faces", totalFaces);
|
||||
invalidate();
|
||||
perf.addPoint("total-vertices", totalVertices);
|
||||
perf.addPoint("total-faces", totalFaces);
|
||||
sceneComponent?.invalidate();
|
||||
};
|
||||
</script>
|
||||
|
||||
{#if $AppSettings.showPerformancePanel}
|
||||
{#if appSettings.debug.showPerformancePanel}
|
||||
<SmallPerformanceViewer {fps} store={perf} />
|
||||
{/if}
|
||||
|
||||
<Canvas>
|
||||
<Scene bind:scene bind:invalidate {lines} {centerCamera} bind:fps />
|
||||
<Scene
|
||||
bind:this={sceneComponent}
|
||||
{lines}
|
||||
{centerCamera}
|
||||
bind:scene
|
||||
bind:fps
|
||||
/>
|
||||
</Canvas>
|
||||
|
@@ -1,6 +1,5 @@
|
||||
import { fastHashArrayBuffer } from "@nodes/utils";
|
||||
import { BufferAttribute, BufferGeometry, Float32BufferAttribute, Group, InstancedMesh, Material, Matrix4, Mesh } from "three"
|
||||
|
||||
import { BufferAttribute, BufferGeometry, Float32BufferAttribute, Group, InstancedMesh, Material, Matrix4, Mesh } from "three";
|
||||
|
||||
function fastArrayHash(arr: ArrayBuffer) {
|
||||
let ints = new Uint8Array(arr);
|
||||
@@ -108,7 +107,6 @@ export function createGeometryPool(parentScene: Group, material: Material) {
|
||||
scene.add(mesh);
|
||||
meshes.push(mesh);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@@ -1,6 +1,5 @@
|
||||
import type { Graph, NodeRegistry, NodeDefinition, RuntimeExecutor, NodeInput } from "@nodes/types";
|
||||
import { concatEncodedArrays, encodeFloat, fastHashArrayBuffer, createLogger, type PerformanceStore } from "@nodes/utils"
|
||||
import type { SyncCache } from "@nodes/types";
|
||||
import type { Graph, NodeDefinition, NodeInput, NodeRegistry, RuntimeExecutor, SyncCache } from "@nodes/types";
|
||||
import { concatEncodedArrays, createLogger, encodeFloat, fastHashArrayBuffer, type PerformanceStore } from "@nodes/utils";
|
||||
|
||||
const log = createLogger("runtime-executor");
|
||||
log.mute()
|
||||
@@ -9,6 +8,7 @@ function getValue(input: NodeInput, value?: unknown) {
|
||||
if (value === undefined && "value" in input) {
|
||||
value = input.value
|
||||
}
|
||||
|
||||
if (input.type === "float") {
|
||||
return encodeFloat(value as number);
|
||||
}
|
||||
|
@@ -5,9 +5,6 @@ import type { Graph, RuntimeExecutor } from "@nodes/types";
|
||||
export class WorkerRuntimeExecutor implements RuntimeExecutor {
|
||||
private worker = new ComlinkWorker<typeof import('./worker-runtime-executor-backend.ts')>(new URL(`./worker-runtime-executor-backend.ts`, import.meta.url));
|
||||
|
||||
constructor() {
|
||||
console.log(import.meta.url)
|
||||
}
|
||||
async execute(graph: Graph, settings: Record<string, unknown>) {
|
||||
return this.worker.executeGraph(graph, settings);
|
||||
}
|
||||
|
@@ -39,6 +39,7 @@ export const AppSettingTypes = {
|
||||
}
|
||||
},
|
||||
debug: {
|
||||
title: "Debug",
|
||||
wireframe: {
|
||||
type: "boolean",
|
||||
label: "Wireframe",
|
||||
@@ -79,7 +80,8 @@ export const AppSettingTypes = {
|
||||
amount: {
|
||||
type: "integer",
|
||||
min: 2,
|
||||
max: 15
|
||||
max: 15,
|
||||
value: 4
|
||||
},
|
||||
loadGrid: {
|
||||
type: "button",
|
||||
@@ -138,8 +140,8 @@ export const appSettings = localState("app-settings", settingsToStore(AppSetting
|
||||
|
||||
$effect.root(() => {
|
||||
$effect(() => {
|
||||
const { theme } = $state.snapshot(appSettings);
|
||||
const classes = document.body.parentElement?.classList;
|
||||
const theme = appSettings.theme;
|
||||
const classes = document.documentElement.classList;
|
||||
const newClassName = `theme-${theme}`;
|
||||
if (classes) {
|
||||
for (const className of classes) {
|
||||
@@ -148,6 +150,6 @@ $effect.root(() => {
|
||||
}
|
||||
}
|
||||
}
|
||||
document.body?.parentElement?.classList.add(newClassName);
|
||||
document.documentElement.classList.add(newClassName);
|
||||
});
|
||||
});
|
||||
|
@@ -1,136 +0,0 @@
|
||||
import localStore from "$lib/helpers/localStore";
|
||||
|
||||
export const AppSettings = localStore("node.settings", {
|
||||
theme: 0,
|
||||
showGrid: true,
|
||||
showNodeGrid: true,
|
||||
snapToGrid: true,
|
||||
showHelp: false,
|
||||
wireframe: false,
|
||||
showIndices: false,
|
||||
showVertices: false,
|
||||
showPerformancePanel: false,
|
||||
showBenchmarkPanel: false,
|
||||
centerCamera: true,
|
||||
showStemLines: false,
|
||||
useWorker: true,
|
||||
amount: 5
|
||||
});
|
||||
|
||||
const themes = ["dark", "light", "catppuccin", "solarized", "high-contrast", "nord", "dracula"];
|
||||
|
||||
AppSettings.subscribe((value) => {
|
||||
const classes = document.body.parentElement?.classList;
|
||||
const newClassName = `theme-${themes[value.theme]}`;
|
||||
if (classes) {
|
||||
for (const className of classes) {
|
||||
if (className.startsWith("theme-") && className !== newClassName) {
|
||||
classes.remove(className);
|
||||
}
|
||||
}
|
||||
}
|
||||
document.body?.parentElement?.classList.add(newClassName);
|
||||
});
|
||||
|
||||
export const AppSettingTypes = {
|
||||
theme: {
|
||||
type: "select",
|
||||
options: themes,
|
||||
label: "Theme",
|
||||
value: themes[0],
|
||||
},
|
||||
showGrid: {
|
||||
type: "boolean",
|
||||
label: "Show Grid",
|
||||
value: true,
|
||||
},
|
||||
centerCamera: {
|
||||
type: "boolean",
|
||||
label: "Center Camera",
|
||||
value: true
|
||||
},
|
||||
nodeInterface: {
|
||||
__title: "Node Interface",
|
||||
showNodeGrid: {
|
||||
type: "boolean",
|
||||
label: "Show Grid",
|
||||
value: true
|
||||
},
|
||||
snapToGrid: {
|
||||
type: "boolean",
|
||||
label: "Snap to Grid",
|
||||
value: true
|
||||
},
|
||||
showHelp: {
|
||||
type: "boolean",
|
||||
label: "Show Help",
|
||||
value: false
|
||||
}
|
||||
},
|
||||
debug: {
|
||||
wireframe: {
|
||||
type: "boolean",
|
||||
label: "Wireframe",
|
||||
value: false,
|
||||
},
|
||||
useWorker: {
|
||||
type: "boolean",
|
||||
label: "Execute runtime in worker",
|
||||
value: true,
|
||||
},
|
||||
showIndices: {
|
||||
type: "boolean",
|
||||
label: "Show Indices",
|
||||
value: false,
|
||||
},
|
||||
showPerformancePanel: {
|
||||
type: "boolean",
|
||||
label: "Show Performance Panel",
|
||||
value: false,
|
||||
},
|
||||
showBenchmarkPanel: {
|
||||
type: "boolean",
|
||||
label: "Show Benchmark Panel",
|
||||
value: false,
|
||||
},
|
||||
showVertices: {
|
||||
type: "boolean",
|
||||
label: "Show Vertices",
|
||||
value: false,
|
||||
},
|
||||
showStemLines: {
|
||||
type: "boolean",
|
||||
label: "Show Stem Lines",
|
||||
value: false,
|
||||
},
|
||||
stressTest: {
|
||||
__title: "Stress Test",
|
||||
amount: {
|
||||
type: "integer",
|
||||
min: 2,
|
||||
max: 15
|
||||
},
|
||||
loadGrid: {
|
||||
type: "button",
|
||||
label: "Load Grid"
|
||||
},
|
||||
loadTree: {
|
||||
type: "button",
|
||||
label: "Load Tree"
|
||||
},
|
||||
lottaFaces: {
|
||||
type: "button",
|
||||
label: "Load 'lots of faces'"
|
||||
},
|
||||
lottaNodes: {
|
||||
type: "button",
|
||||
label: "Load 'lots of nodes'"
|
||||
},
|
||||
lottaNodesAndFaces: {
|
||||
type: "button",
|
||||
label: "Load 'lots of nodes and faces'"
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
92
app/src/lib/settings/panels/ActiveNodeSelected.svelte
Normal file
92
app/src/lib/settings/panels/ActiveNodeSelected.svelte
Normal file
@@ -0,0 +1,92 @@
|
||||
<script lang="ts">
|
||||
import type { Node, NodeInput } from "@nodes/types";
|
||||
import NestedSettings from "./NestedSettings.svelte";
|
||||
import type { GraphManager } from "$lib/graph-interface/graph-manager";
|
||||
|
||||
type Props = {
|
||||
manager: GraphManager;
|
||||
node: Node;
|
||||
};
|
||||
|
||||
const { manager, node }: Props = $props();
|
||||
|
||||
const nodeDefinition = filterInputs(node.tmp?.type?.inputs);
|
||||
function filterInputs(inputs?: Record<string, NodeInput>) {
|
||||
const _inputs = $state.snapshot(inputs);
|
||||
return Object.fromEntries(
|
||||
Object.entries(structuredClone(_inputs ?? {}))
|
||||
.filter(([_key, value]) => {
|
||||
return value.hidden === true;
|
||||
})
|
||||
.map(([key, value]) => {
|
||||
//@ts-ignore
|
||||
value.__node_type = node?.tmp?.type.id;
|
||||
//@ts-ignore
|
||||
value.__node_input = key;
|
||||
return [key, value];
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
type Store = Record<string, number | number[]>;
|
||||
let store = $state<Store>(createStore(node?.props, nodeDefinition));
|
||||
function createStore(
|
||||
props: Node["props"],
|
||||
inputs: Record<string, NodeInput>,
|
||||
): Store {
|
||||
const store: Store = {};
|
||||
Object.keys(inputs).forEach((key) => {
|
||||
if (props) {
|
||||
//@ts-ignore
|
||||
store[key] = props[key] || inputs[key].value;
|
||||
}
|
||||
});
|
||||
return store;
|
||||
}
|
||||
|
||||
let lastPropsHash = "";
|
||||
function updateNode() {
|
||||
if (!node || !store) return;
|
||||
let needsUpdate = false;
|
||||
Object.keys(store).forEach((_key: string) => {
|
||||
node.props = node.props || {};
|
||||
const key = _key as keyof typeof store;
|
||||
if (node && store) {
|
||||
needsUpdate = true;
|
||||
node.props[key] = store[key];
|
||||
}
|
||||
});
|
||||
|
||||
let propsHash = JSON.stringify(node.props);
|
||||
if (propsHash === lastPropsHash) {
|
||||
return;
|
||||
}
|
||||
lastPropsHash = propsHash;
|
||||
|
||||
if (needsUpdate) {
|
||||
manager.execute();
|
||||
}
|
||||
}
|
||||
|
||||
$effect(() => {
|
||||
if (store && store) {
|
||||
updateNode();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
{#if node}
|
||||
{#key node.id}
|
||||
{#if nodeDefinition && store && Object.keys(nodeDefinition).length > 0}
|
||||
<NestedSettings
|
||||
id="activeNodeSettings"
|
||||
bind:value={store}
|
||||
type={nodeDefinition}
|
||||
/>
|
||||
{:else}
|
||||
<p class="mx-4">Active Node has no Settings</p>
|
||||
{/if}
|
||||
{/key}
|
||||
{:else}
|
||||
<p class="mx-4">No active node</p>
|
||||
{/if}
|
@@ -1,85 +1,20 @@
|
||||
<script lang="ts">
|
||||
import type { Node, NodeInput } from "@nodes/types";
|
||||
import NestedSettings from "./NestedSettings.svelte";
|
||||
import { writable } from "svelte/store";
|
||||
|
||||
import type { Node } from "@nodes/types";
|
||||
import type { GraphManager } from "$lib/graph-interface/graph-manager";
|
||||
import ActiveNodeSelected from "./ActiveNodeSelected.svelte";
|
||||
|
||||
export let manager: GraphManager;
|
||||
export let node: Node | undefined;
|
||||
type Props = {
|
||||
manager: GraphManager;
|
||||
node: Node | undefined;
|
||||
};
|
||||
|
||||
function filterInputs(inputs: Record<string, NodeInput>) {
|
||||
return Object.fromEntries(
|
||||
Object.entries(inputs)
|
||||
.filter(([_key, value]) => {
|
||||
return value.hidden === true;
|
||||
})
|
||||
.map(([key, value]) => {
|
||||
//@ts-ignore
|
||||
value.__node_type = node?.tmp?.type.id;
|
||||
//@ts-ignore
|
||||
value.__node_input = key;
|
||||
return [key, value];
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
function createStore(
|
||||
props: Node["props"],
|
||||
inputs: Record<string, NodeInput>,
|
||||
) {
|
||||
const store: Record<string, unknown> = {};
|
||||
Object.keys(inputs).forEach((key) => {
|
||||
if (props) {
|
||||
//@ts-ignore
|
||||
store[key] = props[key] || inputs[key].value;
|
||||
}
|
||||
});
|
||||
return writable(store);
|
||||
}
|
||||
|
||||
let nodeDefinition: Record<string, NodeInput> | undefined;
|
||||
$: nodeDefinition = node?.tmp?.type
|
||||
? filterInputs(node.tmp.type.inputs)
|
||||
: undefined;
|
||||
$: store = node ? createStore(node.props, nodeDefinition) : undefined;
|
||||
|
||||
let lastPropsHash = "";
|
||||
function updateNode() {
|
||||
if (!node || !$store) return;
|
||||
let needsUpdate = false;
|
||||
Object.keys($store).forEach((_key: string) => {
|
||||
node.props = node.props || {};
|
||||
const key = _key as keyof typeof $store;
|
||||
if (node && $store) {
|
||||
needsUpdate = true;
|
||||
node.props[key] = $store[key];
|
||||
}
|
||||
});
|
||||
let propsHash = JSON.stringify(node.props);
|
||||
if (propsHash === lastPropsHash) {
|
||||
return;
|
||||
}
|
||||
lastPropsHash = propsHash;
|
||||
// console.log(needsUpdate, node.props, $store);
|
||||
if (needsUpdate) {
|
||||
manager.execute();
|
||||
}
|
||||
}
|
||||
|
||||
$: if (store && $store) {
|
||||
updateNode();
|
||||
}
|
||||
const { manager, node }: Props = $props();
|
||||
</script>
|
||||
|
||||
{#if node}
|
||||
{#key node.id}
|
||||
{#if nodeDefinition && store && Object.keys(nodeDefinition).length > 0}
|
||||
<NestedSettings
|
||||
id="activeNodeSettings"
|
||||
settings={nodeDefinition}
|
||||
{store}
|
||||
/>
|
||||
{#if node}
|
||||
<ActiveNodeSelected {manager} {node} />
|
||||
{:else}
|
||||
<p class="mx-4">Active Node has no Settings</p>
|
||||
{/if}
|
||||
|
@@ -1,44 +1,49 @@
|
||||
<script lang="ts">
|
||||
import type { createKeyMap } from "$lib/helpers/createKeyMap";
|
||||
import { ShortCut } from "@nodes/ui";
|
||||
import { get } from "svelte/store";
|
||||
|
||||
export let keymap: ReturnType<typeof createKeyMap>;
|
||||
const keys = keymap?.keys;
|
||||
export let title = "Keymap";
|
||||
type Props = {
|
||||
keymaps: {
|
||||
keymap: ReturnType<typeof createKeyMap>;
|
||||
title: string;
|
||||
}[];
|
||||
};
|
||||
|
||||
let { keymaps }: Props = $props();
|
||||
console.log({ keymaps });
|
||||
</script>
|
||||
|
||||
<div class="wrapper">
|
||||
<h3>{title}</h3>
|
||||
|
||||
<section>
|
||||
{#each $keys as key}
|
||||
{#if key.description}
|
||||
<div class="command-wrapper">
|
||||
<ShortCut
|
||||
alt={key.alt}
|
||||
ctrl={key.ctrl}
|
||||
shift={key.shift}
|
||||
key={key.key}
|
||||
/>
|
||||
</div>
|
||||
<p>{key.description}</p>
|
||||
{/if}
|
||||
<table class="wrapper">
|
||||
<tbody>
|
||||
{#each keymaps as keymap}
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<h3>{keymap.title}</h3>
|
||||
</td>
|
||||
</tr>
|
||||
{#each get(keymap.keymap?.keys) as key}
|
||||
<tr>
|
||||
{#if key.description}
|
||||
<td class="command-wrapper">
|
||||
<ShortCut
|
||||
alt={key.alt}
|
||||
ctrl={key.ctrl}
|
||||
shift={key.shift}
|
||||
key={key.key}
|
||||
/>
|
||||
</td>
|
||||
<td>{key.description}</td>
|
||||
{/if}
|
||||
</tr>
|
||||
{/each}
|
||||
{/each}
|
||||
</section>
|
||||
</div>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<style>
|
||||
.wrapper {
|
||||
padding: 1em;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1em;
|
||||
}
|
||||
|
||||
section {
|
||||
display: grid;
|
||||
grid-template-columns: min-content 1fr;
|
||||
gap: 1em;
|
||||
}
|
||||
|
||||
h3 {
|
||||
@@ -51,10 +56,11 @@
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
p {
|
||||
td {
|
||||
font-size: 0.9em;
|
||||
margin: 0;
|
||||
display: flex;
|
||||
padding: 7px;
|
||||
padding-left: 0;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
|
@@ -1,3 +1,7 @@
|
||||
<script module lang="ts">
|
||||
let openSections = localState<Record<string,boolean>>("open-details", {});
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import NestedSettings from "./NestedSettings.svelte";
|
||||
import {localState} from "$lib/helpers/localState.svelte";
|
||||
@@ -11,15 +15,18 @@
|
||||
interface Nested {
|
||||
[key: string]: (Nested & { title?: string }) | InputType;
|
||||
}
|
||||
type SettingsType = Record<string, Nested>;
|
||||
type SettingsValue = Record<string, Record<string, unknown> | string | number | boolean>;
|
||||
|
||||
type Props = {
|
||||
id: string;
|
||||
key?: string;
|
||||
value: Record<string, unknown> | string | number | boolean;
|
||||
type: Nested;
|
||||
value: SettingsValue;
|
||||
type: SettingsType;
|
||||
depth?: number;
|
||||
};
|
||||
|
||||
|
||||
let { id, key = "", value = $bindable(), type, depth = 0 }: Props = $props();
|
||||
|
||||
function isNodeInput(v: InputType | Nested): v is InputType {
|
||||
@@ -28,14 +35,14 @@
|
||||
|
||||
let internalValue = $state(Array.isArray(type?.[key]?.options) ? type[key]?.options?.indexOf(value?.[key]) : value?.[key]);
|
||||
|
||||
let openSections = localState("open-details", {});
|
||||
let open = $state(openSections[id]);
|
||||
if(depth > 0 && !isNodeInput(type[key])){
|
||||
$effect(() => {
|
||||
if(open !== undefined){}
|
||||
openSections[id] = open;
|
||||
});
|
||||
}
|
||||
if(open !== undefined){
|
||||
openSections[id] = open;
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
$effect(() => {
|
||||
@@ -49,7 +56,7 @@
|
||||
</script>
|
||||
|
||||
{#if key && isNodeInput(type?.[key]) }
|
||||
<div class="input input-{type[key].type}">
|
||||
<div class="input input-{type[key].type}" class:first-level={depth === 1}>
|
||||
{#if type[key].type === "button"}
|
||||
<button onclick={() => console.log(type[key])}>
|
||||
{type[key].label || key}
|
||||
@@ -65,27 +72,25 @@
|
||||
<NestedSettings
|
||||
id={`${id}.${childKey}`}
|
||||
key={childKey}
|
||||
value={value as Record<string, unknown>}
|
||||
type={type as Nested}
|
||||
value={value}
|
||||
type={type}
|
||||
depth={depth + 1}
|
||||
/>
|
||||
{/each}
|
||||
{#if depth > 0}
|
||||
<hr />
|
||||
{/if}
|
||||
{:else if key && type?.[key]}
|
||||
{#if depth > 0}
|
||||
<hr />
|
||||
{/if}
|
||||
<details bind:open>
|
||||
<summary>{type[key]?.title||key}</summary>
|
||||
<summary><p>{type[key]?.title||key}</p></summary>
|
||||
<div class="content">
|
||||
{#each Object.keys(type[key]).filter((key) => key !== "title") as childKey}
|
||||
<NestedSettings
|
||||
id={`${id}.${childKey}`}
|
||||
key={childKey}
|
||||
value={value[key] as Record<string, unknown>}
|
||||
type={type[key] as Nested}
|
||||
value={value[key] as SettingsValue}
|
||||
type={type[key] as SettingsType}
|
||||
depth={depth + 1}
|
||||
/>
|
||||
{/each}
|
||||
@@ -103,9 +108,18 @@
|
||||
user-select: none;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
summary::marker { }
|
||||
|
||||
summary > p {
|
||||
display: inline;
|
||||
padding-left: 6px;
|
||||
}
|
||||
|
||||
details {
|
||||
padding: 1em;
|
||||
padding-bottom: 0;
|
||||
padding-left: 21px;
|
||||
}
|
||||
|
||||
.input {
|
||||
@@ -114,7 +128,7 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
padding-left: 14px;
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.input-boolean {
|
||||
@@ -126,16 +140,12 @@
|
||||
order: 2;
|
||||
}
|
||||
|
||||
.first-level > .input {
|
||||
padding-right: 1rem;
|
||||
.first-level.input {
|
||||
padding-left: 1em;
|
||||
padding-right: 1em;
|
||||
padding-bottom: 1px;
|
||||
}
|
||||
|
||||
.first-level {
|
||||
border-bottom: solid thin var(--outline);
|
||||
}
|
||||
.first-level > details {
|
||||
border: none;
|
||||
}
|
||||
hr {
|
||||
position: absolute;
|
||||
margin: 0;
|
||||
|
@@ -1,8 +0,0 @@
|
||||
import type {
|
||||
Graph,
|
||||
Node as NodeType,
|
||||
NodeDefinition,
|
||||
NodeInput,
|
||||
RuntimeExecutor,
|
||||
} from "@nodes/types";
|
||||
export type { Graph, NodeDefinition, NodeInput };
|
@@ -5,12 +5,10 @@
|
||||
import type { Graph, Node } from "@nodes/types";
|
||||
import Viewer from "$lib/result-viewer/Viewer.svelte";
|
||||
import Settings from "$lib/settings/Settings.svelte";
|
||||
import { AppSettingTypes, AppSettings } from "$lib/settings/app-settings";
|
||||
import {
|
||||
appSettings as _appSettings,
|
||||
AppSettingTypes as _AppSettingTypes,
|
||||
appSettings,
|
||||
AppSettingTypes,
|
||||
} from "$lib/settings/app-settings.svelte";
|
||||
import { writable } from "svelte/store";
|
||||
import Keymap from "$lib/settings/panels/Keymap.svelte";
|
||||
import { createKeyMap } from "$lib/helpers/createKeyMap";
|
||||
import NodeStore from "$lib/node-store/NodeStore.svelte";
|
||||
@@ -30,6 +28,7 @@
|
||||
import { createPerformanceStore } from "@nodes/utils";
|
||||
import BenchmarkPanel from "$lib/settings/panels/BenchmarkPanel.svelte";
|
||||
import { debounceAsyncFunction } from "$lib/helpers";
|
||||
import { onMount } from "svelte";
|
||||
|
||||
let performanceStore = createPerformanceStore();
|
||||
|
||||
@@ -41,24 +40,26 @@
|
||||
const memoryRuntime = new MemoryRuntimeExecutor(nodeRegistry, runtimeCache);
|
||||
memoryRuntime.perf = performanceStore;
|
||||
|
||||
$: runtime = $AppSettings.useWorker ? workerRuntime : memoryRuntime;
|
||||
const runtime = $derived(
|
||||
appSettings.debug.useWorker ? workerRuntime : memoryRuntime,
|
||||
);
|
||||
|
||||
let activeNode: Node | undefined;
|
||||
let scene: Group;
|
||||
let updateViewerResult: (result: Int32Array) => void;
|
||||
let activeNode = $state<Node | undefined>(undefined);
|
||||
let scene = $state<Group>(null!);
|
||||
|
||||
let graph = localStorage.getItem("graph")
|
||||
? JSON.parse(localStorage.getItem("graph")!)
|
||||
: templates.defaultPlant;
|
||||
|
||||
let graphInterface: ReturnType<typeof GraphInterface>;
|
||||
$: manager = graphInterface?.manager;
|
||||
$: managerStatus = manager?.status;
|
||||
$: keymap = graphInterface?.keymap;
|
||||
let graphInterface = $state<ReturnType<typeof GraphInterface>>(null!);
|
||||
let viewerComponent = $state<ReturnType<typeof Viewer>>();
|
||||
const manager = $derived(graphInterface?.manager);
|
||||
const managerStatus = $derived(manager?.status);
|
||||
|
||||
async function randomGenerate() {
|
||||
if (!manager) return;
|
||||
const g = manager.serialize();
|
||||
const s = { ...$graphSettings, randomSeed: true };
|
||||
const s = { ...graphSettings, randomSeed: true };
|
||||
await handleUpdate(g, s);
|
||||
}
|
||||
|
||||
@@ -69,18 +70,18 @@
|
||||
callback: randomGenerate,
|
||||
},
|
||||
]);
|
||||
let graphSettings = writable<Record<string, any>>({});
|
||||
let graphSettingTypes = {};
|
||||
let graphSettings = $state<Record<string, any>>({});
|
||||
let graphSettingTypes = $state({});
|
||||
|
||||
const handleUpdate = debounceAsyncFunction(
|
||||
async (g: Graph, s: Record<string, any>) => {
|
||||
async (g: Graph, s: Record<string, any> = graphSettings) => {
|
||||
performanceStore.startRun();
|
||||
try {
|
||||
let a = performance.now();
|
||||
const graphResult = await runtime.execute(g, s);
|
||||
const graphResult = await runtime.execute(g, $state.snapshot(s));
|
||||
let b = performance.now();
|
||||
|
||||
if ($AppSettings.useWorker) {
|
||||
if (appSettings.debug.useWorker) {
|
||||
let perfData = await runtime.getPerformanceData();
|
||||
let lastRun = perfData?.at(-1);
|
||||
if (lastRun?.total) {
|
||||
@@ -94,7 +95,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
updateViewerResult(graphResult);
|
||||
viewerComponent?.update(graphResult);
|
||||
} catch (error) {
|
||||
console.log("errors", error);
|
||||
} finally {
|
||||
@@ -103,32 +104,35 @@
|
||||
},
|
||||
);
|
||||
|
||||
$: if (AppSettings) {
|
||||
//@ts-ignore
|
||||
AppSettingTypes.debug.stressTest.loadGrid.callback = () => {
|
||||
graph = templates.grid($AppSettings.amount, $AppSettings.amount);
|
||||
};
|
||||
//@ts-ignore
|
||||
AppSettingTypes.debug.stressTest.loadTree.callback = () => {
|
||||
graph = templates.tree($AppSettings.amount);
|
||||
};
|
||||
//@ts-ignore
|
||||
AppSettingTypes.debug.stressTest.lottaFaces.callback = () => {
|
||||
graph = templates.lottaFaces;
|
||||
};
|
||||
//@ts-ignore
|
||||
AppSettingTypes.debug.stressTest.lottaNodes.callback = () => {
|
||||
graph = templates.lottaNodes;
|
||||
};
|
||||
//@ts-ignore
|
||||
AppSettingTypes.debug.stressTest.lottaNodesAndFaces.callback = () => {
|
||||
graph = templates.lottaNodesAndFaces;
|
||||
};
|
||||
}
|
||||
// $ if (AppSettings) {
|
||||
// //@ts-ignore
|
||||
// AppSettingTypes.debug.stressTest.loadGrid.callback = () => {
|
||||
// graph = templates.grid($AppSettings.amount, $AppSettings.amount);
|
||||
// };
|
||||
// //@ts-ignore
|
||||
// AppSettingTypes.debug.stressTest.loadTree.callback = () => {
|
||||
// graph = templates.tree($AppSettings.amount);
|
||||
// };
|
||||
// //@ts-ignore
|
||||
// AppSettingTypes.debug.stressTest.lottaFaces.callback = () => {
|
||||
// graph = templates.lottaFaces;
|
||||
// };
|
||||
// //@ts-ignore
|
||||
// AppSettingTypes.debug.stressTest.lottaNodes.callback = () => {
|
||||
// graph = templates.lottaNodes;
|
||||
// };
|
||||
// //@ts-ignore
|
||||
// AppSettingTypes.debug.stressTest.lottaNodesAndFaces.callback = () => {
|
||||
// graph = templates.lottaNodesAndFaces;
|
||||
// };
|
||||
// }
|
||||
|
||||
function handleSave(graph: Graph) {
|
||||
localStorage.setItem("graph", JSON.stringify(graph));
|
||||
}
|
||||
onMount(() => {
|
||||
handleUpdate(graph);
|
||||
});
|
||||
</script>
|
||||
|
||||
<svelte:document on:keydown={applicationKeymap.handleKeyboardEvent} />
|
||||
@@ -137,10 +141,10 @@
|
||||
<Grid.Row>
|
||||
<Grid.Cell>
|
||||
<Viewer
|
||||
perf={performanceStore}
|
||||
bind:scene
|
||||
bind:update={updateViewerResult}
|
||||
centerCamera={$AppSettings.centerCamera}
|
||||
bind:this={viewerComponent}
|
||||
perf={performanceStore}
|
||||
centerCamera={appSettings.centerCamera}
|
||||
/>
|
||||
</Grid.Cell>
|
||||
<Grid.Cell>
|
||||
@@ -149,21 +153,21 @@
|
||||
bind:this={graphInterface}
|
||||
{graph}
|
||||
registry={nodeRegistry}
|
||||
showGrid={appSettings.nodeInterface.showNodeGrid}
|
||||
snapToGrid={appSettings.nodeInterface.snapToGrid}
|
||||
bind:activeNode
|
||||
showGrid={$AppSettings.showNodeGrid}
|
||||
snapToGrid={$AppSettings.snapToGrid}
|
||||
bind:showHelp={$AppSettings.showHelp}
|
||||
bind:showHelp={appSettings.nodeInterface.showHelp}
|
||||
bind:settings={graphSettings}
|
||||
bind:settingTypes={graphSettingTypes}
|
||||
onresult={(result) => handleUpdate(result, $graphSettings)}
|
||||
onresult={(result) => handleUpdate(result)}
|
||||
onsave={(graph) => handleSave(graph)}
|
||||
/>
|
||||
<Settings>
|
||||
<Panel id="general" title="General" icon="i-tabler-settings">
|
||||
<NestedSettings
|
||||
id="general"
|
||||
value={_appSettings}
|
||||
type={_AppSettingTypes}
|
||||
value={appSettings}
|
||||
type={AppSettingTypes}
|
||||
/>
|
||||
</Panel>
|
||||
<Panel
|
||||
@@ -171,10 +175,12 @@
|
||||
title="Keyboard Shortcuts"
|
||||
icon="i-tabler-keyboard"
|
||||
>
|
||||
<Keymap title="Application" keymap={applicationKeymap} />
|
||||
{#if keymap}
|
||||
<Keymap title="Node-Editor" {keymap} />
|
||||
{/if}
|
||||
<Keymap
|
||||
keymaps={[
|
||||
{ keymap: applicationKeymap, title: "Application" },
|
||||
{ keymap: graphInterface.keymap, title: "Node-Editor" },
|
||||
]}
|
||||
/>
|
||||
</Panel>
|
||||
<Panel id="exports" title="Exporter" icon="i-tabler-package-export">
|
||||
<ExportSettings {scene} />
|
||||
@@ -191,7 +197,7 @@
|
||||
id="performance"
|
||||
title="Performance"
|
||||
classes="text-red-400"
|
||||
hidden={!$AppSettings.showPerformancePanel}
|
||||
hidden={!appSettings.debug.showPerformancePanel}
|
||||
icon="i-tabler-brand-speedtest"
|
||||
>
|
||||
{#if $performanceStore}
|
||||
@@ -202,7 +208,7 @@
|
||||
id="benchmark"
|
||||
title="Benchmark"
|
||||
classes="text-red-400"
|
||||
hidden={!$AppSettings.showBenchmarkPanel}
|
||||
hidden={!appSettings.debug.showBenchmarkPanel}
|
||||
icon="i-tabler-graph"
|
||||
>
|
||||
<BenchmarkPanel run={randomGenerate} />
|
||||
|
@@ -10,22 +10,22 @@
|
||||
"strength": {
|
||||
"type": "float",
|
||||
"min": 0,
|
||||
"max": 1,
|
||||
"value": 1
|
||||
"max": 1
|
||||
"value": 1,
|
||||
},
|
||||
"curviness": {
|
||||
"type": "float",
|
||||
"hidden": true,
|
||||
"min": 0,
|
||||
"max": 1,
|
||||
"value": 0.5,
|
||||
"max": 1
|
||||
},
|
||||
"depth": {
|
||||
"type": "integer",
|
||||
"min": 1,
|
||||
"max": 10,
|
||||
"hidden": true,
|
||||
"value": 1,
|
||||
"hidden": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
10
package.json
10
package.json
@@ -1,18 +1,10 @@
|
||||
{
|
||||
"scripts": {
|
||||
"build": "pnpm build:nodes && pnpm build:app",
|
||||
"build:story": "pnpm -r --filter 'ui' story:build",
|
||||
"build:app": "BASE_PATH=/ui pnpm -r --filter 'ui' build && pnpm -r --filter 'app' build",
|
||||
"build:nodes": "pnpm -r --filter './nodes/**' build",
|
||||
"dev:nodes": "pnpm -r --parallel --filter './nodes/**' dev",
|
||||
"build:deploy": "pnpm build",
|
||||
"build:deploy": "pnpm build && cp -r ./packages/ui/storybook-static ./app/build/ui",
|
||||
"dev": "pnpm -r --filter 'app' --filter './packages/node-registry' dev"
|
||||
},
|
||||
"packageManager": "pnpm@9.11.0+sha512.0a203ffaed5a3f63242cd064c8fb5892366c103e328079318f78062f24ea8c9d50bc6a47aa3567cabefd824d170e78fa2745ed1f16b132e16436146b7688f19b",
|
||||
"dependencies": {
|
||||
"@types/pg": "^8.11.10",
|
||||
"drizzle-kit": "^0.30.1",
|
||||
"drizzle-orm": "^0.38.2",
|
||||
"pg": "^8.13.1"
|
||||
}
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { NodeDefinitionSchema, type AsyncCache, type NodeDefinition, type NodeRegistry } from "@nodes/types";
|
||||
import { createLogger, createWasmWrapper } from "@nodes/utils";
|
||||
import { type NodeRegistry, type NodeDefinition, NodeDefinitionSchema, type AsyncCache } from "@nodes/types";
|
||||
import { createWasmWrapper, createLogger } from "@nodes/utils";
|
||||
|
||||
const log = createLogger("node-registry");
|
||||
log.mute();
|
||||
@@ -49,21 +49,18 @@ export class RemoteNodeRegistry implements NodeRegistry {
|
||||
|
||||
private async fetchNodeWasm(nodeId: `${string}/${string}/${string}`) {
|
||||
|
||||
const fetchNode = async () => {
|
||||
const response = await this.fetch(`${this.url}/nodes/${nodeId}.wasm`);
|
||||
return response.arrayBuffer();
|
||||
}
|
||||
|
||||
const res = await Promise.race([
|
||||
fetchNode(),
|
||||
this.cache?.get(nodeId)
|
||||
]);
|
||||
|
||||
if (!res) {
|
||||
const response = await this.fetch(`${this.url}/nodes/${nodeId}.wasm`);
|
||||
if (!response.ok) {
|
||||
if (this.cache) {
|
||||
let value = await this.cache.get(nodeId);
|
||||
if (value) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
throw new Error(`Failed to load node wasm ${nodeId}`);
|
||||
}
|
||||
|
||||
return res;
|
||||
return response.arrayBuffer();
|
||||
}
|
||||
|
||||
async load(nodeIds: `${string}/${string}/${string}`[]) {
|
||||
|
@@ -34,6 +34,6 @@
|
||||
}
|
||||
|
||||
.content {
|
||||
padding-left: 12px;
|
||||
/* padding-left: 12px; */
|
||||
}
|
||||
</style>
|
||||
|
882
pnpm-lock.yaml
generated
882
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -1 +0,0 @@
|
||||
DATABASE_URL=postgres://nodarium:nodarium@postgres-db:5432/nodarium
|
@@ -1,64 +0,0 @@
|
||||
import * as path from "jsr:@std/path";
|
||||
const arg = Deno.args[0];
|
||||
|
||||
const base = arg.startsWith("/") ? arg : path.join(Deno.cwd(), arg);
|
||||
|
||||
const dirs = Deno.readDir(base);
|
||||
|
||||
type Node = {
|
||||
user: string;
|
||||
system: string;
|
||||
id: string;
|
||||
path: string;
|
||||
};
|
||||
|
||||
const nodes: Node[] = [];
|
||||
|
||||
for await (const dir of dirs) {
|
||||
if (dir.isDirectory) {
|
||||
const userDir = path.join(base, dir.name);
|
||||
for await (const userName of Deno.readDir(userDir)) {
|
||||
if (userName.isDirectory) {
|
||||
const nodeSystemDir = path.join(userDir, userName.name);
|
||||
for await (const nodeDir of Deno.readDir(nodeSystemDir)) {
|
||||
if (nodeDir.isDirectory && !nodeDir.name.startsWith(".")) {
|
||||
const wasmFilePath = path.join(
|
||||
nodeSystemDir,
|
||||
nodeDir.name,
|
||||
"pkg",
|
||||
"index_bg.wasm",
|
||||
);
|
||||
nodes.push({
|
||||
user: dir.name,
|
||||
system: userName.name,
|
||||
id: nodeDir.name,
|
||||
path: wasmFilePath,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function postNode(node: Node) {
|
||||
const wasmContent = await Deno.readFile(node.path);
|
||||
|
||||
const url = `http://localhost:8000/v1/nodes`;
|
||||
|
||||
const res = await fetch(url, {
|
||||
method: "POST",
|
||||
body: wasmContent,
|
||||
});
|
||||
|
||||
if (res.ok) {
|
||||
console.log(`Uploaded ${node.id}`);
|
||||
} else {
|
||||
const text = await res.text();
|
||||
console.log(`Failed to upload ${node.id}: ${text}`);
|
||||
}
|
||||
}
|
||||
|
||||
for (const node of nodes) {
|
||||
await postNode(node);
|
||||
}
|
@@ -1,29 +0,0 @@
|
||||
services:
|
||||
app:
|
||||
image: denoland/deno:latest
|
||||
working_dir: /app
|
||||
ports:
|
||||
- 8000:8000
|
||||
environment:
|
||||
DATABASE_URL: postgres://nodarium:nodarium@db:5432/nodarium
|
||||
volumes:
|
||||
- .:/app
|
||||
- deno-cache:/deno-dir/
|
||||
command: run --allow-net --allow-env --allow-read --watch src/main.ts
|
||||
depends_on:
|
||||
- db
|
||||
|
||||
db:
|
||||
image: postgres:latest
|
||||
environment:
|
||||
POSTGRES_USER: nodarium
|
||||
POSTGRES_PASSWORD: nodarium
|
||||
POSTGRES_DB: nodarium
|
||||
ports:
|
||||
- "5432:5432"
|
||||
volumes:
|
||||
- postgres-data:/var/lib/postgresql/data
|
||||
|
||||
volumes:
|
||||
postgres-data:
|
||||
deno-cache:
|
@@ -1,20 +0,0 @@
|
||||
{
|
||||
"tasks": {
|
||||
"dev": "deno run --watch main.ts",
|
||||
"test": "deno run vitest",
|
||||
"drizzle": "podman-compose exec app deno --env -A --node-modules-dir npm:drizzle-kit",
|
||||
"upload": "deno run --allow-read --allow-net bin/upload.ts"
|
||||
},
|
||||
"imports": {
|
||||
"@asteasolutions/zod-to-openapi": "npm:@asteasolutions/zod-to-openapi@^7.3.0",
|
||||
"@hono/swagger-ui": "npm:@hono/swagger-ui@^0.5.0",
|
||||
"@hono/zod-openapi": "npm:@hono/zod-openapi@^0.18.3",
|
||||
"@std/assert": "jsr:@std/assert@1",
|
||||
"@types/pg": "npm:@types/pg@^8.11.10",
|
||||
"drizzle-orm": "npm:drizzle-orm@^0.38.2",
|
||||
"hono": "npm:hono@^4.6.14",
|
||||
"pg": "npm:pg@^8.13.1",
|
||||
"vitest": "npm:vitest@^2.1.8",
|
||||
"zod": "npm:zod@^3.24.1"
|
||||
}
|
||||
}
|
881
store/deno.lock
generated
881
store/deno.lock
generated
@@ -1,881 +0,0 @@
|
||||
{
|
||||
"version": "4",
|
||||
"specifiers": {
|
||||
"jsr:@std/assert@1": "1.0.9",
|
||||
"jsr:@std/assert@^1.0.9": "1.0.9",
|
||||
"jsr:@std/bytes@^1.0.2": "1.0.4",
|
||||
"jsr:@std/crypto@^1.0.3": "1.0.3",
|
||||
"jsr:@std/expect@*": "1.0.9",
|
||||
"jsr:@std/internal@^1.0.5": "1.0.5",
|
||||
"jsr:@std/path@*": "1.0.8",
|
||||
"jsr:@std/uuid@*": "1.0.4",
|
||||
"npm:@asteasolutions/zod-to-openapi@^7.3.0": "7.3.0_zod@3.24.1",
|
||||
"npm:@hono/swagger-ui@0.5": "0.5.0_hono@4.6.14",
|
||||
"npm:@hono/zod-openapi@~0.18.3": "0.18.3_hono@4.6.14_zod@3.24.1",
|
||||
"npm:@types/node@*": "22.5.4",
|
||||
"npm:@types/pg@^8.11.10": "8.11.10",
|
||||
"npm:drizzle-kit@*": "0.30.1_esbuild@0.19.12",
|
||||
"npm:drizzle-orm@~0.38.2": "0.38.2_@types+pg@8.11.10_pg@8.13.1",
|
||||
"npm:hono@^4.6.14": "4.6.14",
|
||||
"npm:pg@^8.13.1": "8.13.1",
|
||||
"npm:vitest@^2.1.8": "2.1.8_vite@5.4.11",
|
||||
"npm:zod@^3.24.1": "3.24.1"
|
||||
},
|
||||
"jsr": {
|
||||
"@std/assert@1.0.9": {
|
||||
"integrity": "a9f0c611a869cc791b26f523eec54c7e187aab7932c2c8e8bea0622d13680dcd",
|
||||
"dependencies": [
|
||||
"jsr:@std/internal"
|
||||
]
|
||||
},
|
||||
"@std/bytes@1.0.4": {
|
||||
"integrity": "11a0debe522707c95c7b7ef89b478c13fb1583a7cfb9a85674cd2cc2e3a28abc"
|
||||
},
|
||||
"@std/crypto@1.0.3": {
|
||||
"integrity": "a2a32f51ddef632d299e3879cd027c630dcd4d1d9a5285d6e6788072f4e51e7f"
|
||||
},
|
||||
"@std/expect@1.0.9": {
|
||||
"integrity": "108bb428f17492ac40439479e1dc55fbaae581530e905a8603f97305842a5a01",
|
||||
"dependencies": [
|
||||
"jsr:@std/assert@^1.0.9",
|
||||
"jsr:@std/internal"
|
||||
]
|
||||
},
|
||||
"@std/internal@1.0.5": {
|
||||
"integrity": "54a546004f769c1ac9e025abd15a76b6671ddc9687e2313b67376125650dc7ba"
|
||||
},
|
||||
"@std/path@1.0.8": {
|
||||
"integrity": "548fa456bb6a04d3c1a1e7477986b6cffbce95102d0bb447c67c4ee70e0364be"
|
||||
},
|
||||
"@std/uuid@1.0.4": {
|
||||
"integrity": "f4233149cc8b4753cc3763fd83a7c4101699491f55c7be78dc7b30281946d7a0",
|
||||
"dependencies": [
|
||||
"jsr:@std/bytes",
|
||||
"jsr:@std/crypto"
|
||||
]
|
||||
}
|
||||
},
|
||||
"npm": {
|
||||
"@asteasolutions/zod-to-openapi@7.3.0_zod@3.24.1": {
|
||||
"integrity": "sha512-7tE/r1gXwMIvGnXVUdIqUhCU1RevEFC4Jk6Bussa0fk1ecbnnINkZzj1EOAJyE/M3AI25DnHT/zKQL1/FPFi8Q==",
|
||||
"dependencies": [
|
||||
"openapi3-ts",
|
||||
"zod"
|
||||
]
|
||||
},
|
||||
"@drizzle-team/brocli@0.10.2": {
|
||||
"integrity": "sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w=="
|
||||
},
|
||||
"@esbuild-kit/core-utils@3.3.2": {
|
||||
"integrity": "sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ==",
|
||||
"dependencies": [
|
||||
"esbuild@0.18.20",
|
||||
"source-map-support"
|
||||
]
|
||||
},
|
||||
"@esbuild-kit/esm-loader@2.6.5": {
|
||||
"integrity": "sha512-FxEMIkJKnodyA1OaCUoEvbYRkoZlLZ4d/eXFu9Fh8CbBBgP5EmZxrfTRyN0qpXZ4vOvqnE5YdRdcrmUUXuU+dA==",
|
||||
"dependencies": [
|
||||
"@esbuild-kit/core-utils",
|
||||
"get-tsconfig"
|
||||
]
|
||||
},
|
||||
"@esbuild/aix-ppc64@0.19.12": {
|
||||
"integrity": "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA=="
|
||||
},
|
||||
"@esbuild/aix-ppc64@0.21.5": {
|
||||
"integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ=="
|
||||
},
|
||||
"@esbuild/android-arm64@0.18.20": {
|
||||
"integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ=="
|
||||
},
|
||||
"@esbuild/android-arm64@0.19.12": {
|
||||
"integrity": "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA=="
|
||||
},
|
||||
"@esbuild/android-arm64@0.21.5": {
|
||||
"integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A=="
|
||||
},
|
||||
"@esbuild/android-arm@0.18.20": {
|
||||
"integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw=="
|
||||
},
|
||||
"@esbuild/android-arm@0.19.12": {
|
||||
"integrity": "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w=="
|
||||
},
|
||||
"@esbuild/android-arm@0.21.5": {
|
||||
"integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg=="
|
||||
},
|
||||
"@esbuild/android-x64@0.18.20": {
|
||||
"integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg=="
|
||||
},
|
||||
"@esbuild/android-x64@0.19.12": {
|
||||
"integrity": "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew=="
|
||||
},
|
||||
"@esbuild/android-x64@0.21.5": {
|
||||
"integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA=="
|
||||
},
|
||||
"@esbuild/darwin-arm64@0.18.20": {
|
||||
"integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA=="
|
||||
},
|
||||
"@esbuild/darwin-arm64@0.19.12": {
|
||||
"integrity": "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g=="
|
||||
},
|
||||
"@esbuild/darwin-arm64@0.21.5": {
|
||||
"integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ=="
|
||||
},
|
||||
"@esbuild/darwin-x64@0.18.20": {
|
||||
"integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ=="
|
||||
},
|
||||
"@esbuild/darwin-x64@0.19.12": {
|
||||
"integrity": "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A=="
|
||||
},
|
||||
"@esbuild/darwin-x64@0.21.5": {
|
||||
"integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw=="
|
||||
},
|
||||
"@esbuild/freebsd-arm64@0.18.20": {
|
||||
"integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw=="
|
||||
},
|
||||
"@esbuild/freebsd-arm64@0.19.12": {
|
||||
"integrity": "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA=="
|
||||
},
|
||||
"@esbuild/freebsd-arm64@0.21.5": {
|
||||
"integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g=="
|
||||
},
|
||||
"@esbuild/freebsd-x64@0.18.20": {
|
||||
"integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ=="
|
||||
},
|
||||
"@esbuild/freebsd-x64@0.19.12": {
|
||||
"integrity": "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg=="
|
||||
},
|
||||
"@esbuild/freebsd-x64@0.21.5": {
|
||||
"integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ=="
|
||||
},
|
||||
"@esbuild/linux-arm64@0.18.20": {
|
||||
"integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA=="
|
||||
},
|
||||
"@esbuild/linux-arm64@0.19.12": {
|
||||
"integrity": "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA=="
|
||||
},
|
||||
"@esbuild/linux-arm64@0.21.5": {
|
||||
"integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q=="
|
||||
},
|
||||
"@esbuild/linux-arm@0.18.20": {
|
||||
"integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg=="
|
||||
},
|
||||
"@esbuild/linux-arm@0.19.12": {
|
||||
"integrity": "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w=="
|
||||
},
|
||||
"@esbuild/linux-arm@0.21.5": {
|
||||
"integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA=="
|
||||
},
|
||||
"@esbuild/linux-ia32@0.18.20": {
|
||||
"integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA=="
|
||||
},
|
||||
"@esbuild/linux-ia32@0.19.12": {
|
||||
"integrity": "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA=="
|
||||
},
|
||||
"@esbuild/linux-ia32@0.21.5": {
|
||||
"integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg=="
|
||||
},
|
||||
"@esbuild/linux-loong64@0.18.20": {
|
||||
"integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg=="
|
||||
},
|
||||
"@esbuild/linux-loong64@0.19.12": {
|
||||
"integrity": "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA=="
|
||||
},
|
||||
"@esbuild/linux-loong64@0.21.5": {
|
||||
"integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg=="
|
||||
},
|
||||
"@esbuild/linux-mips64el@0.18.20": {
|
||||
"integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ=="
|
||||
},
|
||||
"@esbuild/linux-mips64el@0.19.12": {
|
||||
"integrity": "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w=="
|
||||
},
|
||||
"@esbuild/linux-mips64el@0.21.5": {
|
||||
"integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg=="
|
||||
},
|
||||
"@esbuild/linux-ppc64@0.18.20": {
|
||||
"integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA=="
|
||||
},
|
||||
"@esbuild/linux-ppc64@0.19.12": {
|
||||
"integrity": "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg=="
|
||||
},
|
||||
"@esbuild/linux-ppc64@0.21.5": {
|
||||
"integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w=="
|
||||
},
|
||||
"@esbuild/linux-riscv64@0.18.20": {
|
||||
"integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A=="
|
||||
},
|
||||
"@esbuild/linux-riscv64@0.19.12": {
|
||||
"integrity": "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg=="
|
||||
},
|
||||
"@esbuild/linux-riscv64@0.21.5": {
|
||||
"integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA=="
|
||||
},
|
||||
"@esbuild/linux-s390x@0.18.20": {
|
||||
"integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ=="
|
||||
},
|
||||
"@esbuild/linux-s390x@0.19.12": {
|
||||
"integrity": "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg=="
|
||||
},
|
||||
"@esbuild/linux-s390x@0.21.5": {
|
||||
"integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A=="
|
||||
},
|
||||
"@esbuild/linux-x64@0.18.20": {
|
||||
"integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w=="
|
||||
},
|
||||
"@esbuild/linux-x64@0.19.12": {
|
||||
"integrity": "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg=="
|
||||
},
|
||||
"@esbuild/linux-x64@0.21.5": {
|
||||
"integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ=="
|
||||
},
|
||||
"@esbuild/netbsd-x64@0.18.20": {
|
||||
"integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A=="
|
||||
},
|
||||
"@esbuild/netbsd-x64@0.19.12": {
|
||||
"integrity": "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA=="
|
||||
},
|
||||
"@esbuild/netbsd-x64@0.21.5": {
|
||||
"integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg=="
|
||||
},
|
||||
"@esbuild/openbsd-x64@0.18.20": {
|
||||
"integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg=="
|
||||
},
|
||||
"@esbuild/openbsd-x64@0.19.12": {
|
||||
"integrity": "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw=="
|
||||
},
|
||||
"@esbuild/openbsd-x64@0.21.5": {
|
||||
"integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow=="
|
||||
},
|
||||
"@esbuild/sunos-x64@0.18.20": {
|
||||
"integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ=="
|
||||
},
|
||||
"@esbuild/sunos-x64@0.19.12": {
|
||||
"integrity": "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA=="
|
||||
},
|
||||
"@esbuild/sunos-x64@0.21.5": {
|
||||
"integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg=="
|
||||
},
|
||||
"@esbuild/win32-arm64@0.18.20": {
|
||||
"integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg=="
|
||||
},
|
||||
"@esbuild/win32-arm64@0.19.12": {
|
||||
"integrity": "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A=="
|
||||
},
|
||||
"@esbuild/win32-arm64@0.21.5": {
|
||||
"integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A=="
|
||||
},
|
||||
"@esbuild/win32-ia32@0.18.20": {
|
||||
"integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g=="
|
||||
},
|
||||
"@esbuild/win32-ia32@0.19.12": {
|
||||
"integrity": "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ=="
|
||||
},
|
||||
"@esbuild/win32-ia32@0.21.5": {
|
||||
"integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA=="
|
||||
},
|
||||
"@esbuild/win32-x64@0.18.20": {
|
||||
"integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ=="
|
||||
},
|
||||
"@esbuild/win32-x64@0.19.12": {
|
||||
"integrity": "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA=="
|
||||
},
|
||||
"@esbuild/win32-x64@0.21.5": {
|
||||
"integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw=="
|
||||
},
|
||||
"@hono/swagger-ui@0.5.0_hono@4.6.14": {
|
||||
"integrity": "sha512-MWYYSv9kC8IwFBLZdwgZZMT9zUq2C/4/ekuyEYOkHEgUMqu+FG3eebtBZ4ofMh60xYRxRR2BgQGoNIILys/PFg==",
|
||||
"dependencies": [
|
||||
"hono"
|
||||
]
|
||||
},
|
||||
"@hono/zod-openapi@0.18.3_hono@4.6.14_zod@3.24.1": {
|
||||
"integrity": "sha512-bNlRDODnp7P9Fs13ZPajEOt13G0XwXKfKRHMEFCphQsFiD1Y+twzHaglpNAhNcflzR1DQwHY92ZS06b4LTPbIQ==",
|
||||
"dependencies": [
|
||||
"@asteasolutions/zod-to-openapi",
|
||||
"@hono/zod-validator",
|
||||
"hono",
|
||||
"zod"
|
||||
]
|
||||
},
|
||||
"@hono/zod-validator@0.4.2_hono@4.6.14_zod@3.24.1": {
|
||||
"integrity": "sha512-1rrlBg+EpDPhzOV4hT9pxr5+xDVmKuz6YJl+la7VCwK6ass5ldyKm5fD+umJdV2zhHD6jROoCCv8NbTwyfhT0g==",
|
||||
"dependencies": [
|
||||
"hono",
|
||||
"zod"
|
||||
]
|
||||
},
|
||||
"@jridgewell/sourcemap-codec@1.5.0": {
|
||||
"integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ=="
|
||||
},
|
||||
"@rollup/rollup-android-arm-eabi@4.28.1": {
|
||||
"integrity": "sha512-2aZp8AES04KI2dy3Ss6/MDjXbwBzj+i0GqKtWXgw2/Ma6E4jJvujryO6gJAghIRVz7Vwr9Gtl/8na3nDUKpraQ=="
|
||||
},
|
||||
"@rollup/rollup-android-arm64@4.28.1": {
|
||||
"integrity": "sha512-EbkK285O+1YMrg57xVA+Dp0tDBRB93/BZKph9XhMjezf6F4TpYjaUSuPt5J0fZXlSag0LmZAsTmdGGqPp4pQFA=="
|
||||
},
|
||||
"@rollup/rollup-darwin-arm64@4.28.1": {
|
||||
"integrity": "sha512-prduvrMKU6NzMq6nxzQw445zXgaDBbMQvmKSJaxpaZ5R1QDM8w+eGxo6Y/jhT/cLoCvnZI42oEqf9KQNYz1fqQ=="
|
||||
},
|
||||
"@rollup/rollup-darwin-x64@4.28.1": {
|
||||
"integrity": "sha512-WsvbOunsUk0wccO/TV4o7IKgloJ942hVFK1CLatwv6TJspcCZb9umQkPdvB7FihmdxgaKR5JyxDjWpCOp4uZlQ=="
|
||||
},
|
||||
"@rollup/rollup-freebsd-arm64@4.28.1": {
|
||||
"integrity": "sha512-HTDPdY1caUcU4qK23FeeGxCdJF64cKkqajU0iBnTVxS8F7H/7BewvYoG+va1KPSL63kQ1PGNyiwKOfReavzvNA=="
|
||||
},
|
||||
"@rollup/rollup-freebsd-x64@4.28.1": {
|
||||
"integrity": "sha512-m/uYasxkUevcFTeRSM9TeLyPe2QDuqtjkeoTpP9SW0XxUWfcYrGDMkO/m2tTw+4NMAF9P2fU3Mw4ahNvo7QmsQ=="
|
||||
},
|
||||
"@rollup/rollup-linux-arm-gnueabihf@4.28.1": {
|
||||
"integrity": "sha512-QAg11ZIt6mcmzpNE6JZBpKfJaKkqTm1A9+y9O+frdZJEuhQxiugM05gnCWiANHj4RmbgeVJpTdmKRmH/a+0QbA=="
|
||||
},
|
||||
"@rollup/rollup-linux-arm-musleabihf@4.28.1": {
|
||||
"integrity": "sha512-dRP9PEBfolq1dmMcFqbEPSd9VlRuVWEGSmbxVEfiq2cs2jlZAl0YNxFzAQS2OrQmsLBLAATDMb3Z6MFv5vOcXg=="
|
||||
},
|
||||
"@rollup/rollup-linux-arm64-gnu@4.28.1": {
|
||||
"integrity": "sha512-uGr8khxO+CKT4XU8ZUH1TTEUtlktK6Kgtv0+6bIFSeiSlnGJHG1tSFSjm41uQ9sAO/5ULx9mWOz70jYLyv1QkA=="
|
||||
},
|
||||
"@rollup/rollup-linux-arm64-musl@4.28.1": {
|
||||
"integrity": "sha512-QF54q8MYGAqMLrX2t7tNpi01nvq5RI59UBNx+3+37zoKX5KViPo/gk2QLhsuqok05sSCRluj0D00LzCwBikb0A=="
|
||||
},
|
||||
"@rollup/rollup-linux-loongarch64-gnu@4.28.1": {
|
||||
"integrity": "sha512-vPul4uodvWvLhRco2w0GcyZcdyBfpfDRgNKU+p35AWEbJ/HPs1tOUrkSueVbBS0RQHAf/A+nNtDpvw95PeVKOA=="
|
||||
},
|
||||
"@rollup/rollup-linux-powerpc64le-gnu@4.28.1": {
|
||||
"integrity": "sha512-pTnTdBuC2+pt1Rmm2SV7JWRqzhYpEILML4PKODqLz+C7Ou2apEV52h19CR7es+u04KlqplggmN9sqZlekg3R1A=="
|
||||
},
|
||||
"@rollup/rollup-linux-riscv64-gnu@4.28.1": {
|
||||
"integrity": "sha512-vWXy1Nfg7TPBSuAncfInmAI/WZDd5vOklyLJDdIRKABcZWojNDY0NJwruY2AcnCLnRJKSaBgf/GiJfauu8cQZA=="
|
||||
},
|
||||
"@rollup/rollup-linux-s390x-gnu@4.28.1": {
|
||||
"integrity": "sha512-/yqC2Y53oZjb0yz8PVuGOQQNOTwxcizudunl/tFs1aLvObTclTwZ0JhXF2XcPT/zuaymemCDSuuUPXJJyqeDOg=="
|
||||
},
|
||||
"@rollup/rollup-linux-x64-gnu@4.28.1": {
|
||||
"integrity": "sha512-fzgeABz7rrAlKYB0y2kSEiURrI0691CSL0+KXwKwhxvj92VULEDQLpBYLHpF49MSiPG4sq5CK3qHMnb9tlCjBw=="
|
||||
},
|
||||
"@rollup/rollup-linux-x64-musl@4.28.1": {
|
||||
"integrity": "sha512-xQTDVzSGiMlSshpJCtudbWyRfLaNiVPXt1WgdWTwWz9n0U12cI2ZVtWe/Jgwyv/6wjL7b66uu61Vg0POWVfz4g=="
|
||||
},
|
||||
"@rollup/rollup-win32-arm64-msvc@4.28.1": {
|
||||
"integrity": "sha512-wSXmDRVupJstFP7elGMgv+2HqXelQhuNf+IS4V+nUpNVi/GUiBgDmfwD0UGN3pcAnWsgKG3I52wMOBnk1VHr/A=="
|
||||
},
|
||||
"@rollup/rollup-win32-ia32-msvc@4.28.1": {
|
||||
"integrity": "sha512-ZkyTJ/9vkgrE/Rk9vhMXhf8l9D+eAhbAVbsGsXKy2ohmJaWg0LPQLnIxRdRp/bKyr8tXuPlXhIoGlEB5XpJnGA=="
|
||||
},
|
||||
"@rollup/rollup-win32-x64-msvc@4.28.1": {
|
||||
"integrity": "sha512-ZvK2jBafvttJjoIdKm/Q/Bh7IJ1Ose9IBOwpOXcOvW3ikGTQGmKDgxTC6oCAzW6PynbkKP8+um1du81XJHZ0JA=="
|
||||
},
|
||||
"@types/estree@1.0.6": {
|
||||
"integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw=="
|
||||
},
|
||||
"@types/node@22.5.4": {
|
||||
"integrity": "sha512-FDuKUJQm/ju9fT/SeX/6+gBzoPzlVCzfzmGkwKvRHQVxi4BntVbyIwf6a4Xn62mrvndLiml6z/UBXIdEVjQLXg==",
|
||||
"dependencies": [
|
||||
"undici-types"
|
||||
]
|
||||
},
|
||||
"@types/pg@8.11.10": {
|
||||
"integrity": "sha512-LczQUW4dbOQzsH2RQ5qoeJ6qJPdrcM/DcMLoqWQkMLMsq83J5lAX3LXjdkWdpscFy67JSOWDnh7Ny/sPFykmkg==",
|
||||
"dependencies": [
|
||||
"@types/node",
|
||||
"pg-protocol",
|
||||
"pg-types@4.0.2"
|
||||
]
|
||||
},
|
||||
"@vitest/expect@2.1.8": {
|
||||
"integrity": "sha512-8ytZ/fFHq2g4PJVAtDX57mayemKgDR6X3Oa2Foro+EygiOJHUXhCqBAAKQYYajZpFoIfvBCF1j6R6IYRSIUFuw==",
|
||||
"dependencies": [
|
||||
"@vitest/spy",
|
||||
"@vitest/utils",
|
||||
"chai",
|
||||
"tinyrainbow"
|
||||
]
|
||||
},
|
||||
"@vitest/mocker@2.1.8_vite@5.4.11": {
|
||||
"integrity": "sha512-7guJ/47I6uqfttp33mgo6ga5Gr1VnL58rcqYKyShoRK9ebu8T5Rs6HN3s1NABiBeVTdWNrwUMcHH54uXZBN4zA==",
|
||||
"dependencies": [
|
||||
"@vitest/spy",
|
||||
"estree-walker",
|
||||
"magic-string",
|
||||
"vite"
|
||||
]
|
||||
},
|
||||
"@vitest/pretty-format@2.1.8": {
|
||||
"integrity": "sha512-9HiSZ9zpqNLKlbIDRWOnAWqgcA7xu+8YxXSekhr0Ykab7PAYFkhkwoqVArPOtJhPmYeE2YHgKZlj3CP36z2AJQ==",
|
||||
"dependencies": [
|
||||
"tinyrainbow"
|
||||
]
|
||||
},
|
||||
"@vitest/runner@2.1.8": {
|
||||
"integrity": "sha512-17ub8vQstRnRlIU5k50bG+QOMLHRhYPAna5tw8tYbj+jzjcspnwnwtPtiOlkuKC4+ixDPTuLZiqiWWQ2PSXHVg==",
|
||||
"dependencies": [
|
||||
"@vitest/utils",
|
||||
"pathe"
|
||||
]
|
||||
},
|
||||
"@vitest/snapshot@2.1.8": {
|
||||
"integrity": "sha512-20T7xRFbmnkfcmgVEz+z3AU/3b0cEzZOt/zmnvZEctg64/QZbSDJEVm9fLnnlSi74KibmRsO9/Qabi+t0vCRPg==",
|
||||
"dependencies": [
|
||||
"@vitest/pretty-format",
|
||||
"magic-string",
|
||||
"pathe"
|
||||
]
|
||||
},
|
||||
"@vitest/spy@2.1.8": {
|
||||
"integrity": "sha512-5swjf2q95gXeYPevtW0BLk6H8+bPlMb4Vw/9Em4hFxDcaOxS+e0LOX4yqNxoHzMR2akEB2xfpnWUzkZokmgWDg==",
|
||||
"dependencies": [
|
||||
"tinyspy"
|
||||
]
|
||||
},
|
||||
"@vitest/utils@2.1.8": {
|
||||
"integrity": "sha512-dwSoui6djdwbfFmIgbIjX2ZhIoG7Ex/+xpxyiEgIGzjliY8xGkcpITKTlp6B4MgtGkF2ilvm97cPM96XZaAgcA==",
|
||||
"dependencies": [
|
||||
"@vitest/pretty-format",
|
||||
"loupe",
|
||||
"tinyrainbow"
|
||||
]
|
||||
},
|
||||
"assertion-error@2.0.1": {
|
||||
"integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA=="
|
||||
},
|
||||
"buffer-from@1.1.2": {
|
||||
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="
|
||||
},
|
||||
"cac@6.7.14": {
|
||||
"integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ=="
|
||||
},
|
||||
"chai@5.1.2": {
|
||||
"integrity": "sha512-aGtmf24DW6MLHHG5gCx4zaI3uBq3KRtxeVs0DjFH6Z0rDNbsvTxFASFvdj79pxjxZ8/5u3PIiN3IwEIQkiiuPw==",
|
||||
"dependencies": [
|
||||
"assertion-error",
|
||||
"check-error",
|
||||
"deep-eql",
|
||||
"loupe",
|
||||
"pathval"
|
||||
]
|
||||
},
|
||||
"check-error@2.1.1": {
|
||||
"integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw=="
|
||||
},
|
||||
"debug@4.4.0": {
|
||||
"integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
|
||||
"dependencies": [
|
||||
"ms"
|
||||
]
|
||||
},
|
||||
"deep-eql@5.0.2": {
|
||||
"integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q=="
|
||||
},
|
||||
"drizzle-kit@0.30.1_esbuild@0.19.12": {
|
||||
"integrity": "sha512-HmA/NeewvHywhJ2ENXD3KvOuM/+K2dGLJfxVfIHsGwaqKICJnS+Ke2L6UcSrSrtMJLJaT0Im1Qv4TFXfaZShyw==",
|
||||
"dependencies": [
|
||||
"@drizzle-team/brocli",
|
||||
"@esbuild-kit/esm-loader",
|
||||
"esbuild@0.19.12",
|
||||
"esbuild-register"
|
||||
]
|
||||
},
|
||||
"drizzle-orm@0.38.2_@types+pg@8.11.10_pg@8.13.1": {
|
||||
"integrity": "sha512-eCE3yPRAskLo1WpM9OHpFaM70tBEDsWhwR/0M3CKyztAXKR9Qs3asZlcJOEliIcUSg8GuwrlY0dmYDgmm6y5GQ==",
|
||||
"dependencies": [
|
||||
"@types/pg",
|
||||
"pg"
|
||||
]
|
||||
},
|
||||
"es-module-lexer@1.5.4": {
|
||||
"integrity": "sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw=="
|
||||
},
|
||||
"esbuild-register@3.6.0_esbuild@0.19.12": {
|
||||
"integrity": "sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg==",
|
||||
"dependencies": [
|
||||
"debug",
|
||||
"esbuild@0.19.12"
|
||||
]
|
||||
},
|
||||
"esbuild@0.18.20": {
|
||||
"integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==",
|
||||
"dependencies": [
|
||||
"@esbuild/android-arm@0.18.20",
|
||||
"@esbuild/android-arm64@0.18.20",
|
||||
"@esbuild/android-x64@0.18.20",
|
||||
"@esbuild/darwin-arm64@0.18.20",
|
||||
"@esbuild/darwin-x64@0.18.20",
|
||||
"@esbuild/freebsd-arm64@0.18.20",
|
||||
"@esbuild/freebsd-x64@0.18.20",
|
||||
"@esbuild/linux-arm@0.18.20",
|
||||
"@esbuild/linux-arm64@0.18.20",
|
||||
"@esbuild/linux-ia32@0.18.20",
|
||||
"@esbuild/linux-loong64@0.18.20",
|
||||
"@esbuild/linux-mips64el@0.18.20",
|
||||
"@esbuild/linux-ppc64@0.18.20",
|
||||
"@esbuild/linux-riscv64@0.18.20",
|
||||
"@esbuild/linux-s390x@0.18.20",
|
||||
"@esbuild/linux-x64@0.18.20",
|
||||
"@esbuild/netbsd-x64@0.18.20",
|
||||
"@esbuild/openbsd-x64@0.18.20",
|
||||
"@esbuild/sunos-x64@0.18.20",
|
||||
"@esbuild/win32-arm64@0.18.20",
|
||||
"@esbuild/win32-ia32@0.18.20",
|
||||
"@esbuild/win32-x64@0.18.20"
|
||||
]
|
||||
},
|
||||
"esbuild@0.19.12": {
|
||||
"integrity": "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==",
|
||||
"dependencies": [
|
||||
"@esbuild/aix-ppc64@0.19.12",
|
||||
"@esbuild/android-arm@0.19.12",
|
||||
"@esbuild/android-arm64@0.19.12",
|
||||
"@esbuild/android-x64@0.19.12",
|
||||
"@esbuild/darwin-arm64@0.19.12",
|
||||
"@esbuild/darwin-x64@0.19.12",
|
||||
"@esbuild/freebsd-arm64@0.19.12",
|
||||
"@esbuild/freebsd-x64@0.19.12",
|
||||
"@esbuild/linux-arm@0.19.12",
|
||||
"@esbuild/linux-arm64@0.19.12",
|
||||
"@esbuild/linux-ia32@0.19.12",
|
||||
"@esbuild/linux-loong64@0.19.12",
|
||||
"@esbuild/linux-mips64el@0.19.12",
|
||||
"@esbuild/linux-ppc64@0.19.12",
|
||||
"@esbuild/linux-riscv64@0.19.12",
|
||||
"@esbuild/linux-s390x@0.19.12",
|
||||
"@esbuild/linux-x64@0.19.12",
|
||||
"@esbuild/netbsd-x64@0.19.12",
|
||||
"@esbuild/openbsd-x64@0.19.12",
|
||||
"@esbuild/sunos-x64@0.19.12",
|
||||
"@esbuild/win32-arm64@0.19.12",
|
||||
"@esbuild/win32-ia32@0.19.12",
|
||||
"@esbuild/win32-x64@0.19.12"
|
||||
]
|
||||
},
|
||||
"esbuild@0.21.5": {
|
||||
"integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==",
|
||||
"dependencies": [
|
||||
"@esbuild/aix-ppc64@0.21.5",
|
||||
"@esbuild/android-arm@0.21.5",
|
||||
"@esbuild/android-arm64@0.21.5",
|
||||
"@esbuild/android-x64@0.21.5",
|
||||
"@esbuild/darwin-arm64@0.21.5",
|
||||
"@esbuild/darwin-x64@0.21.5",
|
||||
"@esbuild/freebsd-arm64@0.21.5",
|
||||
"@esbuild/freebsd-x64@0.21.5",
|
||||
"@esbuild/linux-arm@0.21.5",
|
||||
"@esbuild/linux-arm64@0.21.5",
|
||||
"@esbuild/linux-ia32@0.21.5",
|
||||
"@esbuild/linux-loong64@0.21.5",
|
||||
"@esbuild/linux-mips64el@0.21.5",
|
||||
"@esbuild/linux-ppc64@0.21.5",
|
||||
"@esbuild/linux-riscv64@0.21.5",
|
||||
"@esbuild/linux-s390x@0.21.5",
|
||||
"@esbuild/linux-x64@0.21.5",
|
||||
"@esbuild/netbsd-x64@0.21.5",
|
||||
"@esbuild/openbsd-x64@0.21.5",
|
||||
"@esbuild/sunos-x64@0.21.5",
|
||||
"@esbuild/win32-arm64@0.21.5",
|
||||
"@esbuild/win32-ia32@0.21.5",
|
||||
"@esbuild/win32-x64@0.21.5"
|
||||
]
|
||||
},
|
||||
"estree-walker@3.0.3": {
|
||||
"integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
|
||||
"dependencies": [
|
||||
"@types/estree"
|
||||
]
|
||||
},
|
||||
"expect-type@1.1.0": {
|
||||
"integrity": "sha512-bFi65yM+xZgk+u/KRIpekdSYkTB5W1pEf0Lt8Q8Msh7b+eQ7LXVtIB1Bkm4fvclDEL1b2CZkMhv2mOeF8tMdkA=="
|
||||
},
|
||||
"fsevents@2.3.3": {
|
||||
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="
|
||||
},
|
||||
"get-tsconfig@4.8.1": {
|
||||
"integrity": "sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==",
|
||||
"dependencies": [
|
||||
"resolve-pkg-maps"
|
||||
]
|
||||
},
|
||||
"hono@4.6.14": {
|
||||
"integrity": "sha512-j4VkyUp2xazGJ8eCCLN1Vm/bxdvm/j5ZuU9AIjLu9vapn2M44p9L3Ktr9Vnb2RN2QtcR/wVjZVMlT5k7GJQgPw=="
|
||||
},
|
||||
"loupe@3.1.2": {
|
||||
"integrity": "sha512-23I4pFZHmAemUnz8WZXbYRSKYj801VDaNv9ETuMh7IrMc7VuVVSo+Z9iLE3ni30+U48iDWfi30d3twAXBYmnCg=="
|
||||
},
|
||||
"magic-string@0.30.17": {
|
||||
"integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==",
|
||||
"dependencies": [
|
||||
"@jridgewell/sourcemap-codec"
|
||||
]
|
||||
},
|
||||
"ms@2.1.3": {
|
||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
|
||||
},
|
||||
"nanoid@3.3.8": {
|
||||
"integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w=="
|
||||
},
|
||||
"obuf@1.1.2": {
|
||||
"integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg=="
|
||||
},
|
||||
"openapi3-ts@4.4.0": {
|
||||
"integrity": "sha512-9asTNB9IkKEzWMcHmVZE7Ts3kC9G7AFHfs8i7caD8HbI76gEjdkId4z/AkP83xdZsH7PLAnnbl47qZkXuxpArw==",
|
||||
"dependencies": [
|
||||
"yaml"
|
||||
]
|
||||
},
|
||||
"pathe@1.1.2": {
|
||||
"integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ=="
|
||||
},
|
||||
"pathval@2.0.0": {
|
||||
"integrity": "sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA=="
|
||||
},
|
||||
"pg-cloudflare@1.1.1": {
|
||||
"integrity": "sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q=="
|
||||
},
|
||||
"pg-connection-string@2.7.0": {
|
||||
"integrity": "sha512-PI2W9mv53rXJQEOb8xNR8lH7Hr+EKa6oJa38zsK0S/ky2er16ios1wLKhZyxzD7jUReiWokc9WK5nxSnC7W1TA=="
|
||||
},
|
||||
"pg-int8@1.0.1": {
|
||||
"integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw=="
|
||||
},
|
||||
"pg-numeric@1.0.2": {
|
||||
"integrity": "sha512-BM/Thnrw5jm2kKLE5uJkXqqExRUY/toLHda65XgFTBTFYZyopbKjBe29Ii3RbkvlsMoFwD+tHeGaCjjv0gHlyw=="
|
||||
},
|
||||
"pg-pool@3.7.0_pg@8.13.1": {
|
||||
"integrity": "sha512-ZOBQForurqh4zZWjrgSwwAtzJ7QiRX0ovFkZr2klsen3Nm0aoh33Ls0fzfv3imeH/nw/O27cjdz5kzYJfeGp/g==",
|
||||
"dependencies": [
|
||||
"pg"
|
||||
]
|
||||
},
|
||||
"pg-protocol@1.7.0": {
|
||||
"integrity": "sha512-hTK/mE36i8fDDhgDFjy6xNOG+LCorxLG3WO17tku+ij6sVHXh1jQUJ8hYAnRhNla4QVD2H8er/FOjc/+EgC6yQ=="
|
||||
},
|
||||
"pg-types@2.2.0": {
|
||||
"integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==",
|
||||
"dependencies": [
|
||||
"pg-int8",
|
||||
"postgres-array@2.0.0",
|
||||
"postgres-bytea@1.0.0",
|
||||
"postgres-date@1.0.7",
|
||||
"postgres-interval@1.2.0"
|
||||
]
|
||||
},
|
||||
"pg-types@4.0.2": {
|
||||
"integrity": "sha512-cRL3JpS3lKMGsKaWndugWQoLOCoP+Cic8oseVcbr0qhPzYD5DWXK+RZ9LY9wxRf7RQia4SCwQlXk0q6FCPrVng==",
|
||||
"dependencies": [
|
||||
"pg-int8",
|
||||
"pg-numeric",
|
||||
"postgres-array@3.0.2",
|
||||
"postgres-bytea@3.0.0",
|
||||
"postgres-date@2.1.0",
|
||||
"postgres-interval@3.0.0",
|
||||
"postgres-range"
|
||||
]
|
||||
},
|
||||
"pg@8.13.1": {
|
||||
"integrity": "sha512-OUir1A0rPNZlX//c7ksiu7crsGZTKSOXJPgtNiHGIlC9H0lO+NC6ZDYksSgBYY/thSWhnSRBv8w1lieNNGATNQ==",
|
||||
"dependencies": [
|
||||
"pg-cloudflare",
|
||||
"pg-connection-string",
|
||||
"pg-pool",
|
||||
"pg-protocol",
|
||||
"pg-types@2.2.0",
|
||||
"pgpass"
|
||||
]
|
||||
},
|
||||
"pgpass@1.0.5": {
|
||||
"integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==",
|
||||
"dependencies": [
|
||||
"split2"
|
||||
]
|
||||
},
|
||||
"picocolors@1.1.1": {
|
||||
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="
|
||||
},
|
||||
"postcss@8.4.49": {
|
||||
"integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==",
|
||||
"dependencies": [
|
||||
"nanoid",
|
||||
"picocolors",
|
||||
"source-map-js"
|
||||
]
|
||||
},
|
||||
"postgres-array@2.0.0": {
|
||||
"integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA=="
|
||||
},
|
||||
"postgres-array@3.0.2": {
|
||||
"integrity": "sha512-6faShkdFugNQCLwucjPcY5ARoW1SlbnrZjmGl0IrrqewpvxvhSLHimCVzqeuULCbG0fQv7Dtk1yDbG3xv7Veog=="
|
||||
},
|
||||
"postgres-bytea@1.0.0": {
|
||||
"integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w=="
|
||||
},
|
||||
"postgres-bytea@3.0.0": {
|
||||
"integrity": "sha512-CNd4jim9RFPkObHSjVHlVrxoVQXz7quwNFpz7RY1okNNme49+sVyiTvTRobiLV548Hx/hb1BG+iE7h9493WzFw==",
|
||||
"dependencies": [
|
||||
"obuf"
|
||||
]
|
||||
},
|
||||
"postgres-date@1.0.7": {
|
||||
"integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q=="
|
||||
},
|
||||
"postgres-date@2.1.0": {
|
||||
"integrity": "sha512-K7Juri8gtgXVcDfZttFKVmhglp7epKb1K4pgrkLxehjqkrgPhfG6OO8LHLkfaqkbpjNRnra018XwAr1yQFWGcA=="
|
||||
},
|
||||
"postgres-interval@1.2.0": {
|
||||
"integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==",
|
||||
"dependencies": [
|
||||
"xtend"
|
||||
]
|
||||
},
|
||||
"postgres-interval@3.0.0": {
|
||||
"integrity": "sha512-BSNDnbyZCXSxgA+1f5UU2GmwhoI0aU5yMxRGO8CdFEcY2BQF9xm/7MqKnYoM1nJDk8nONNWDk9WeSmePFhQdlw=="
|
||||
},
|
||||
"postgres-range@1.1.4": {
|
||||
"integrity": "sha512-i/hbxIE9803Alj/6ytL7UHQxRvZkI9O4Sy+J3HGc4F4oo/2eQAjTSNJ0bfxyse3bH0nuVesCk+3IRLaMtG3H6w=="
|
||||
},
|
||||
"resolve-pkg-maps@1.0.0": {
|
||||
"integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw=="
|
||||
},
|
||||
"rollup@4.28.1": {
|
||||
"integrity": "sha512-61fXYl/qNVinKmGSTHAZ6Yy8I3YIJC/r2m9feHo6SwVAVcLT5MPwOUFe7EuURA/4m0NR8lXG4BBXuo/IZEsjMg==",
|
||||
"dependencies": [
|
||||
"@rollup/rollup-android-arm-eabi",
|
||||
"@rollup/rollup-android-arm64",
|
||||
"@rollup/rollup-darwin-arm64",
|
||||
"@rollup/rollup-darwin-x64",
|
||||
"@rollup/rollup-freebsd-arm64",
|
||||
"@rollup/rollup-freebsd-x64",
|
||||
"@rollup/rollup-linux-arm-gnueabihf",
|
||||
"@rollup/rollup-linux-arm-musleabihf",
|
||||
"@rollup/rollup-linux-arm64-gnu",
|
||||
"@rollup/rollup-linux-arm64-musl",
|
||||
"@rollup/rollup-linux-loongarch64-gnu",
|
||||
"@rollup/rollup-linux-powerpc64le-gnu",
|
||||
"@rollup/rollup-linux-riscv64-gnu",
|
||||
"@rollup/rollup-linux-s390x-gnu",
|
||||
"@rollup/rollup-linux-x64-gnu",
|
||||
"@rollup/rollup-linux-x64-musl",
|
||||
"@rollup/rollup-win32-arm64-msvc",
|
||||
"@rollup/rollup-win32-ia32-msvc",
|
||||
"@rollup/rollup-win32-x64-msvc",
|
||||
"@types/estree",
|
||||
"fsevents"
|
||||
]
|
||||
},
|
||||
"siginfo@2.0.0": {
|
||||
"integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g=="
|
||||
},
|
||||
"source-map-js@1.2.1": {
|
||||
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="
|
||||
},
|
||||
"source-map-support@0.5.21": {
|
||||
"integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
|
||||
"dependencies": [
|
||||
"buffer-from",
|
||||
"source-map"
|
||||
]
|
||||
},
|
||||
"source-map@0.6.1": {
|
||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
|
||||
},
|
||||
"split2@4.2.0": {
|
||||
"integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg=="
|
||||
},
|
||||
"stackback@0.0.2": {
|
||||
"integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw=="
|
||||
},
|
||||
"std-env@3.8.0": {
|
||||
"integrity": "sha512-Bc3YwwCB+OzldMxOXJIIvC6cPRWr/LxOp48CdQTOkPyk/t4JWWJbrilwBd7RJzKV8QW7tJkcgAmeuLLJugl5/w=="
|
||||
},
|
||||
"tinybench@2.9.0": {
|
||||
"integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg=="
|
||||
},
|
||||
"tinyexec@0.3.1": {
|
||||
"integrity": "sha512-WiCJLEECkO18gwqIp6+hJg0//p23HXp4S+gGtAKu3mI2F2/sXC4FvHvXvB0zJVVaTPhx1/tOwdbRsa1sOBIKqQ=="
|
||||
},
|
||||
"tinypool@1.0.2": {
|
||||
"integrity": "sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA=="
|
||||
},
|
||||
"tinyrainbow@1.2.0": {
|
||||
"integrity": "sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ=="
|
||||
},
|
||||
"tinyspy@3.0.2": {
|
||||
"integrity": "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q=="
|
||||
},
|
||||
"undici-types@6.19.8": {
|
||||
"integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw=="
|
||||
},
|
||||
"vite-node@2.1.8": {
|
||||
"integrity": "sha512-uPAwSr57kYjAUux+8E2j0q0Fxpn8M9VoyfGiRI8Kfktz9NcYMCenwY5RnZxnF1WTu3TGiYipirIzacLL3VVGFg==",
|
||||
"dependencies": [
|
||||
"cac",
|
||||
"debug",
|
||||
"es-module-lexer",
|
||||
"pathe",
|
||||
"vite"
|
||||
]
|
||||
},
|
||||
"vite@5.4.11": {
|
||||
"integrity": "sha512-c7jFQRklXua0mTzneGW9QVyxFjUgwcihC4bXEtujIo2ouWCe1Ajt/amn2PCxYnhYfd5k09JX3SB7OYWFKYqj8Q==",
|
||||
"dependencies": [
|
||||
"esbuild@0.21.5",
|
||||
"fsevents",
|
||||
"postcss",
|
||||
"rollup"
|
||||
]
|
||||
},
|
||||
"vitest@2.1.8_vite@5.4.11": {
|
||||
"integrity": "sha512-1vBKTZskHw/aosXqQUlVWWlGUxSJR8YtiyZDJAFeW2kPAeX6S3Sool0mjspO+kXLuxVWlEDDowBAeqeAQefqLQ==",
|
||||
"dependencies": [
|
||||
"@vitest/expect",
|
||||
"@vitest/mocker",
|
||||
"@vitest/pretty-format",
|
||||
"@vitest/runner",
|
||||
"@vitest/snapshot",
|
||||
"@vitest/spy",
|
||||
"@vitest/utils",
|
||||
"chai",
|
||||
"debug",
|
||||
"expect-type",
|
||||
"magic-string",
|
||||
"pathe",
|
||||
"std-env",
|
||||
"tinybench",
|
||||
"tinyexec",
|
||||
"tinypool",
|
||||
"tinyrainbow",
|
||||
"vite",
|
||||
"vite-node",
|
||||
"why-is-node-running"
|
||||
]
|
||||
},
|
||||
"why-is-node-running@2.3.0": {
|
||||
"integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==",
|
||||
"dependencies": [
|
||||
"siginfo",
|
||||
"stackback"
|
||||
]
|
||||
},
|
||||
"xtend@4.0.2": {
|
||||
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="
|
||||
},
|
||||
"yaml@2.6.1": {
|
||||
"integrity": "sha512-7r0XPzioN/Q9kXBro/XPnA6kznR73DHq+GXh5ON7ZozRO6aMjbmiBuKste2wslTFkC5d1dw0GooOCepZXJ2SAg=="
|
||||
},
|
||||
"zod@3.24.1": {
|
||||
"integrity": "sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A=="
|
||||
}
|
||||
},
|
||||
"workspace": {
|
||||
"dependencies": [
|
||||
"jsr:@std/assert@1",
|
||||
"npm:@asteasolutions/zod-to-openapi@^7.3.0",
|
||||
"npm:@hono/swagger-ui@0.5",
|
||||
"npm:@hono/zod-openapi@~0.18.3",
|
||||
"npm:@types/pg@^8.11.10",
|
||||
"npm:drizzle-orm@~0.38.2",
|
||||
"npm:hono@^4.6.14",
|
||||
"npm:pg@^8.13.1",
|
||||
"npm:vitest@^2.1.8",
|
||||
"npm:zod@^3.24.1"
|
||||
]
|
||||
}
|
||||
}
|
@@ -1,11 +0,0 @@
|
||||
import { defineConfig } from "drizzle-kit";
|
||||
|
||||
export default defineConfig({
|
||||
out: "./drizzle",
|
||||
schema: "./src/db/schema.ts",
|
||||
dialect: "postgresql",
|
||||
dbCredentials: {
|
||||
url: Deno.env.get("DATABASE_URL")!,
|
||||
},
|
||||
});
|
||||
|
@@ -1,10 +0,0 @@
|
||||
CREATE TABLE "nodes" (
|
||||
"id" serial NOT NULL,
|
||||
"content" "bytea" NOT NULL,
|
||||
"definition" json NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "users" (
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"name" text
|
||||
);
|
@@ -1,75 +0,0 @@
|
||||
{
|
||||
"id": "53dea8d7-01be-4983-ac75-9de9c9a7f592",
|
||||
"prevId": "00000000-0000-0000-0000-000000000000",
|
||||
"version": "7",
|
||||
"dialect": "postgresql",
|
||||
"tables": {
|
||||
"public.nodes": {
|
||||
"name": "nodes",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "serial",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"content": {
|
||||
"name": "content",
|
||||
"type": "bytea",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"definition": {
|
||||
"name": "definition",
|
||||
"type": "json",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.users": {
|
||||
"name": "users",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "serial",
|
||||
"primaryKey": true,
|
||||
"notNull": true
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
}
|
||||
},
|
||||
"enums": {},
|
||||
"schemas": {},
|
||||
"sequences": {},
|
||||
"roles": {},
|
||||
"policies": {},
|
||||
"views": {},
|
||||
"_meta": {
|
||||
"columns": {},
|
||||
"schemas": {},
|
||||
"tables": {}
|
||||
}
|
||||
}
|
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"version": "7",
|
||||
"dialect": "postgresql",
|
||||
"entries": [
|
||||
{
|
||||
"idx": 0,
|
||||
"version": "7",
|
||||
"when": 1734446124519,
|
||||
"tag": "0000_dark_squirrel_girl",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
@@ -1,15 +0,0 @@
|
||||
import { drizzle } from "drizzle-orm/node-postgres";
|
||||
import pg from "pg";
|
||||
import * as schema from "./schema.ts";
|
||||
|
||||
// Use pg driver.
|
||||
const { Pool } = pg;
|
||||
|
||||
// Instantiate Drizzle client with pg driver and schema.
|
||||
export const db = drizzle({
|
||||
client: new Pool({
|
||||
connectionString: Deno.env.get("DATABASE_URL"),
|
||||
}),
|
||||
schema,
|
||||
});
|
||||
|
@@ -1,2 +0,0 @@
|
||||
export * from "../routes/user/user.schema.ts";
|
||||
export * from "../routes/node/schemas/node.schema.ts";
|
@@ -1,25 +0,0 @@
|
||||
import { OpenAPIHono } from "@hono/zod-openapi";
|
||||
import { router } from "./routes/router.ts";
|
||||
import { createUser } from "./routes/user/user.service.ts";
|
||||
import { swaggerUI } from "@hono/swagger-ui";
|
||||
import { logger } from "hono/logger";
|
||||
import { cors } from "hono/cors";
|
||||
|
||||
await createUser("max");
|
||||
|
||||
const app = new OpenAPIHono();
|
||||
app.use("/v1/*", cors());
|
||||
app.use(logger());
|
||||
app.route("v1", router);
|
||||
|
||||
app.doc("/openapi.json", {
|
||||
openapi: "3.0.0",
|
||||
info: {
|
||||
version: "1.0.0",
|
||||
title: "Nodarium API",
|
||||
},
|
||||
});
|
||||
|
||||
app.get("/ui", swaggerUI({ url: "/openapi.json" }));
|
||||
|
||||
Deno.serve(app.fetch);
|
@@ -1,172 +0,0 @@
|
||||
import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
|
||||
import { HTTPException } from "hono/http-exception";
|
||||
import { idRegex, NodeDefinitionSchema } from "./schemas/types.ts";
|
||||
import * as service from "./node.service.ts";
|
||||
import { bodyLimit } from "hono/body-limit";
|
||||
|
||||
const nodeRouter = new OpenAPIHono();
|
||||
|
||||
const SingleParam = (name: string) =>
|
||||
z
|
||||
.string()
|
||||
.min(3)
|
||||
.max(20)
|
||||
.refine(
|
||||
(value) => idRegex.test(value),
|
||||
"Name should contain only alphabets",
|
||||
)
|
||||
.openapi({ param: { name, in: "path" } });
|
||||
|
||||
const ParamsSchema = z.object({
|
||||
user: SingleParam("user"),
|
||||
system: SingleParam("system"),
|
||||
nodeId: SingleParam("nodeId"),
|
||||
});
|
||||
|
||||
const getUserNodesRoute = createRoute({
|
||||
method: "get",
|
||||
path: "/{user}.json",
|
||||
request: {
|
||||
params: z.object({
|
||||
user: SingleParam("user"),
|
||||
}),
|
||||
},
|
||||
responses: {
|
||||
200: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: z.array(NodeDefinitionSchema),
|
||||
},
|
||||
},
|
||||
description: "Retrieve a single node definition",
|
||||
},
|
||||
},
|
||||
});
|
||||
nodeRouter.openapi(getUserNodesRoute, async (c) => {
|
||||
const userId = c.req.param("user.json").replace(/\.json$/, "");
|
||||
const nodes = await service.getNodeDefinitionsByUser(userId);
|
||||
return c.json(nodes);
|
||||
});
|
||||
|
||||
const getNodeCollectionRoute = createRoute({
|
||||
method: "get",
|
||||
path: "/{user}/{system}.json",
|
||||
request: {
|
||||
params: z.object({
|
||||
user: SingleParam("user"),
|
||||
system: SingleParam("system").optional(),
|
||||
}),
|
||||
},
|
||||
responses: {
|
||||
200: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: z.array(NodeDefinitionSchema),
|
||||
},
|
||||
},
|
||||
description: "Retrieve a single node definition",
|
||||
},
|
||||
},
|
||||
});
|
||||
nodeRouter.openapi(getNodeCollectionRoute, async (c) => {
|
||||
const { user } = c.req.valid("param");
|
||||
const nodeSystemId = c.req.param("system.json").replace(/\.json$/, "");
|
||||
|
||||
const nodes = await service.getNodesBySystem(user, nodeSystemId);
|
||||
return c.json(nodes);
|
||||
});
|
||||
|
||||
const getNodeDefinitionRoute = createRoute({
|
||||
method: "get",
|
||||
path: "/{user}/{system}/{nodeId}{.+\\.json}",
|
||||
request: {
|
||||
params: ParamsSchema,
|
||||
},
|
||||
responses: {
|
||||
200: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: NodeDefinitionSchema,
|
||||
},
|
||||
},
|
||||
description: "Retrieve a single node definition",
|
||||
},
|
||||
},
|
||||
});
|
||||
nodeRouter.openapi(getNodeDefinitionRoute, async (c) => {
|
||||
const { user, system, nodeId } = c.req.valid("param");
|
||||
|
||||
const node = await service.getNodeDefinitionById(
|
||||
user,
|
||||
system,
|
||||
nodeId.replace(/\.json$/, ""),
|
||||
);
|
||||
|
||||
if (!node) {
|
||||
throw new HTTPException(404);
|
||||
}
|
||||
|
||||
return c.json(node);
|
||||
});
|
||||
|
||||
const getNodeWasmRoute = createRoute({
|
||||
method: "get",
|
||||
path: "/{user}/{system}/{nodeId}{.+\\.wasm}",
|
||||
request: {
|
||||
params: ParamsSchema,
|
||||
},
|
||||
responses: {
|
||||
200: {
|
||||
content: {
|
||||
"application/wasm": {
|
||||
schema: z.any(),
|
||||
},
|
||||
},
|
||||
description: "Retrieve a single node",
|
||||
},
|
||||
},
|
||||
});
|
||||
nodeRouter.openapi(getNodeWasmRoute, async (c) => {
|
||||
const { user, system, nodeId } = c.req.valid("param");
|
||||
|
||||
const wasmContent = await service.getNodeWasmById(
|
||||
user,
|
||||
system,
|
||||
nodeId.replace(/\.wasm/, ""),
|
||||
);
|
||||
|
||||
c.header("Content-Type", "application/wasm");
|
||||
|
||||
return c.body(wasmContent);
|
||||
});
|
||||
|
||||
const createNodeRoute = createRoute({
|
||||
method: "post",
|
||||
path: "/",
|
||||
responses: {
|
||||
200: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: NodeDefinitionSchema,
|
||||
},
|
||||
},
|
||||
description: "Create a single node",
|
||||
},
|
||||
},
|
||||
middleware: [
|
||||
bodyLimit({
|
||||
maxSize: 128 * 1024, // 128kb
|
||||
onError: (c) => {
|
||||
return c.text("Node content too large", 413);
|
||||
},
|
||||
}),
|
||||
],
|
||||
});
|
||||
nodeRouter.openapi(createNodeRoute, async (c) => {
|
||||
const buffer = await c.req.arrayBuffer();
|
||||
const bytes = await (await c.req.blob()).bytes();
|
||||
const node = await service.createNode(buffer, bytes);
|
||||
return c.json(node);
|
||||
});
|
||||
|
||||
export { nodeRouter };
|
@@ -1,155 +0,0 @@
|
||||
import { db } from "../../db/db.ts";
|
||||
import { nodeTable } from "./schemas/node.schema.ts";
|
||||
import { NodeDefinition, NodeDefinitionSchema } from "./schemas/types.ts";
|
||||
import { and, eq } from "drizzle-orm";
|
||||
import { createHash } from "node:crypto";
|
||||
import { WorkerMessage } from "./worker/types.ts";
|
||||
|
||||
export type CreateNodeDTO = {
|
||||
id: string;
|
||||
system: string;
|
||||
user: string;
|
||||
content: ArrayBuffer;
|
||||
};
|
||||
|
||||
function getNodeHash(content: Uint8Array) {
|
||||
const hash = createHash("sha256");
|
||||
hash.update(content);
|
||||
return hash.digest("hex").slice(0, 8);
|
||||
}
|
||||
|
||||
function extractDefinition(content: ArrayBuffer): Promise<NodeDefinition> {
|
||||
const worker = new Worker(
|
||||
new URL("./worker/node.worker.ts", import.meta.url).href,
|
||||
{
|
||||
type: "module",
|
||||
},
|
||||
) as Worker & {
|
||||
postMessage: (message: WorkerMessage) => void;
|
||||
};
|
||||
|
||||
return new Promise((res, rej) => {
|
||||
worker.postMessage({ action: "extract-definition", content });
|
||||
setTimeout(() => {
|
||||
worker.terminate();
|
||||
rej(new Error("Worker timeout out"));
|
||||
}, 100);
|
||||
worker.onmessage = function (e) {
|
||||
switch (e.data.action) {
|
||||
case "result":
|
||||
res(e.data.result);
|
||||
break;
|
||||
case "error":
|
||||
console.log("Worker error", e.data.error);
|
||||
rej(e.data.result);
|
||||
break;
|
||||
default:
|
||||
rej(new Error("Unknown worker response"));
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
export async function createNode(
|
||||
wasmBuffer: ArrayBuffer,
|
||||
content: Uint8Array,
|
||||
): Promise<NodeDefinition> {
|
||||
try {
|
||||
const def = await extractDefinition(wasmBuffer);
|
||||
|
||||
const [userId, systemId, nodeId] = def.id.split("/");
|
||||
|
||||
const node: typeof nodeTable.$inferInsert = {
|
||||
userId,
|
||||
systemId,
|
||||
nodeId,
|
||||
definition: def,
|
||||
hash: getNodeHash(content),
|
||||
content: content,
|
||||
};
|
||||
|
||||
await db.insert(nodeTable).values(node);
|
||||
console.log("new node created!");
|
||||
return def;
|
||||
} catch (error) {
|
||||
console.log("Creation Error", { error });
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export function getNodeDefinitionsByUser(userName: string) {
|
||||
return db.select({ definition: nodeTable.definition }).from(nodeTable)
|
||||
.where(
|
||||
and(
|
||||
eq(nodeTable.userId, userName),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
export async function getNodesBySystem(
|
||||
username: string,
|
||||
systemId: string,
|
||||
): Promise<NodeDefinition[]> {
|
||||
const nodes = await db
|
||||
.select()
|
||||
.from(nodeTable)
|
||||
.where(
|
||||
and(eq(nodeTable.systemId, systemId), eq(nodeTable.userId, username)),
|
||||
);
|
||||
|
||||
const definitions = nodes
|
||||
.map((node) => NodeDefinitionSchema.safeParse(node.definition))
|
||||
.filter((v) => v.success)
|
||||
.map((v) => v.data);
|
||||
|
||||
return definitions;
|
||||
}
|
||||
|
||||
export async function getNodeWasmById(
|
||||
userName: string,
|
||||
systemId: string,
|
||||
nodeId: string,
|
||||
) {
|
||||
const node = await db.select({ content: nodeTable.content }).from(nodeTable)
|
||||
.where(
|
||||
and(
|
||||
eq(nodeTable.userId, userName),
|
||||
eq(nodeTable.systemId, systemId),
|
||||
eq(nodeTable.nodeId, nodeId),
|
||||
),
|
||||
).limit(1);
|
||||
|
||||
if (!node[0]) {
|
||||
throw new Error("Node not found");
|
||||
}
|
||||
|
||||
return node[0].content;
|
||||
}
|
||||
|
||||
export async function getNodeDefinitionById(
|
||||
userName: string,
|
||||
systemId: string,
|
||||
nodeId: string,
|
||||
) {
|
||||
const node = await db.select({ definition: nodeTable.definition }).from(
|
||||
nodeTable,
|
||||
).where(
|
||||
and(
|
||||
eq(nodeTable.userId, userName),
|
||||
eq(nodeTable.systemId, systemId),
|
||||
eq(nodeTable.nodeId, nodeId),
|
||||
),
|
||||
).limit(1);
|
||||
|
||||
if (!node[0]) {
|
||||
return;
|
||||
}
|
||||
|
||||
const definition = NodeDefinitionSchema.safeParse(node[0]?.definition);
|
||||
|
||||
if (!definition.data) {
|
||||
throw new Error("Invalid definition");
|
||||
}
|
||||
|
||||
return definition.data;
|
||||
}
|
@@ -1,11 +0,0 @@
|
||||
import { expect } from "jsr:@std/expect";
|
||||
import { router } from "../router.ts";
|
||||
|
||||
Deno.test("simple test", async () => {
|
||||
const res = await router.request("/max/plants/test.json");
|
||||
const json = await res.text();
|
||||
|
||||
expect(true).toEqual(true);
|
||||
|
||||
expect(json).toEqual({ hello: "world" });
|
||||
});
|
@@ -1,81 +0,0 @@
|
||||
import { z } from "@hono/zod-openapi";
|
||||
|
||||
const DefaultOptionsSchema = z.object({
|
||||
internal: z.boolean().optional(),
|
||||
external: z.boolean().optional(),
|
||||
setting: z.string().optional(),
|
||||
label: z.string().optional(),
|
||||
description: z.string().optional(),
|
||||
accepts: z.array(z.string()).optional(),
|
||||
hidden: z.boolean().optional(),
|
||||
});
|
||||
|
||||
const NodeInputFloatSchema = z.object({
|
||||
...DefaultOptionsSchema.shape,
|
||||
type: z.literal("float"),
|
||||
element: z.literal("slider").optional(),
|
||||
value: z.number().optional(),
|
||||
min: z.number().optional(),
|
||||
max: z.number().optional(),
|
||||
step: z.number().optional(),
|
||||
});
|
||||
|
||||
const NodeInputIntegerSchema = z.object({
|
||||
...DefaultOptionsSchema.shape,
|
||||
type: z.literal("integer"),
|
||||
element: z.literal("slider").optional(),
|
||||
value: z.number().optional(),
|
||||
min: z.number().optional(),
|
||||
max: z.number().optional(),
|
||||
});
|
||||
|
||||
const NodeInputBooleanSchema = z.object({
|
||||
...DefaultOptionsSchema.shape,
|
||||
type: z.literal("boolean"),
|
||||
value: z.boolean().optional(),
|
||||
});
|
||||
|
||||
const NodeInputSelectSchema = z.object({
|
||||
...DefaultOptionsSchema.shape,
|
||||
type: z.literal("select"),
|
||||
options: z.array(z.string()).optional(),
|
||||
value: z.number().optional(),
|
||||
});
|
||||
|
||||
const NodeInputSeedSchema = z.object({
|
||||
...DefaultOptionsSchema.shape,
|
||||
type: z.literal("seed"),
|
||||
value: z.number().optional(),
|
||||
});
|
||||
|
||||
const NodeInputVec3Schema = z.object({
|
||||
...DefaultOptionsSchema.shape,
|
||||
type: z.literal("vec3"),
|
||||
value: z.array(z.number()).optional(),
|
||||
});
|
||||
|
||||
const NodeInputGeometrySchema = z.object({
|
||||
...DefaultOptionsSchema.shape,
|
||||
type: z.literal("geometry"),
|
||||
});
|
||||
|
||||
const NodeInputPathSchema = z.object({
|
||||
...DefaultOptionsSchema.shape,
|
||||
type: z.literal("path"),
|
||||
});
|
||||
|
||||
export const NodeInputSchema = z
|
||||
.union([
|
||||
NodeInputSeedSchema,
|
||||
NodeInputBooleanSchema,
|
||||
NodeInputFloatSchema,
|
||||
NodeInputIntegerSchema,
|
||||
NodeInputSelectSchema,
|
||||
NodeInputSeedSchema,
|
||||
NodeInputVec3Schema,
|
||||
NodeInputGeometrySchema,
|
||||
NodeInputPathSchema,
|
||||
])
|
||||
.openapi("NodeInput");
|
||||
|
||||
export type NodeInput = z.infer<typeof NodeInputSchema>;
|
@@ -1,41 +0,0 @@
|
||||
import {
|
||||
customType,
|
||||
integer,
|
||||
json,
|
||||
pgTable,
|
||||
serial,
|
||||
varchar,
|
||||
} from "drizzle-orm/pg-core";
|
||||
import { relations } from "drizzle-orm/relations";
|
||||
import { usersTable } from "../../user/user.schema.ts";
|
||||
|
||||
const bytea = customType<{
|
||||
data: ArrayBuffer;
|
||||
default: false;
|
||||
}>({
|
||||
dataType() {
|
||||
return "bytea";
|
||||
},
|
||||
});
|
||||
|
||||
export const nodeTable = pgTable("nodes", {
|
||||
id: serial().primaryKey(),
|
||||
userId: varchar().notNull(),
|
||||
systemId: varchar().notNull(),
|
||||
nodeId: varchar().notNull(),
|
||||
content: bytea().notNull(),
|
||||
definition: json().notNull(),
|
||||
hash: varchar({ length: 8 }).notNull(),
|
||||
previous: integer(),
|
||||
});
|
||||
|
||||
export const nodeRelations = relations(nodeTable, ({ one }) => ({
|
||||
userId: one(usersTable, {
|
||||
fields: [nodeTable.userId],
|
||||
references: [usersTable.id],
|
||||
}),
|
||||
previous: one(nodeTable, {
|
||||
fields: [nodeTable.previous],
|
||||
references: [nodeTable.id],
|
||||
}),
|
||||
}));
|
@@ -1,31 +0,0 @@
|
||||
import { z } from "zod";
|
||||
import { NodeInputSchema } from "./inputs.ts";
|
||||
|
||||
export type NodeId = `${string}/${string}/${string}`;
|
||||
|
||||
export const idRegex = /[a-z0-9-]+/i;
|
||||
|
||||
const idSchema = z
|
||||
.string()
|
||||
.regex(
|
||||
new RegExp(
|
||||
`^(${idRegex.source})/(${idRegex.source})/(${idRegex.source})$`,
|
||||
),
|
||||
"Invalid id format",
|
||||
);
|
||||
|
||||
export const NodeDefinitionSchema = z
|
||||
.object({
|
||||
id: idSchema,
|
||||
inputs: z.record(NodeInputSchema).optional(),
|
||||
outputs: z.array(z.string()).optional(),
|
||||
meta: z
|
||||
.object({
|
||||
description: z.string().optional(),
|
||||
title: z.string().optional(),
|
||||
})
|
||||
.optional(),
|
||||
})
|
||||
.openapi("NodeDefinition");
|
||||
|
||||
export type NodeDefinition = z.infer<typeof NodeDefinitionSchema>;
|
@@ -1,40 +0,0 @@
|
||||
/// <reference lib="webworker" />
|
||||
|
||||
import { NodeDefinitionSchema } from "../schemas/types.ts";
|
||||
import { WorkerMessage } from "./types.ts";
|
||||
import { createWasmWrapper } from "./utils.ts";
|
||||
|
||||
const workerSelf = self as DedicatedWorkerGlobalScope & {
|
||||
postMessage: (message: WorkerMessage) => void;
|
||||
};
|
||||
|
||||
function extractDefinition(wasmCode: ArrayBuffer) {
|
||||
try {
|
||||
const wasm = createWasmWrapper(wasmCode);
|
||||
|
||||
const definition = wasm.get_definition();
|
||||
|
||||
const p = NodeDefinitionSchema.safeParse(definition);
|
||||
|
||||
if (!p.success) {
|
||||
workerSelf.postMessage({ action: "error", error: p.error });
|
||||
return;
|
||||
}
|
||||
|
||||
workerSelf.postMessage({ action: "result", result: p.data });
|
||||
} catch (e) {
|
||||
console.log("HEEERE", e);
|
||||
workerSelf.postMessage({ action: "error", error: e });
|
||||
}
|
||||
}
|
||||
|
||||
self.onmessage = (e: MessageEvent<WorkerMessage>) => {
|
||||
switch (e.data.action) {
|
||||
case "extract-definition":
|
||||
extractDefinition(e.data.content);
|
||||
self.close();
|
||||
break;
|
||||
default:
|
||||
throw new Error("Unknown action: " + e.data.action);
|
||||
}
|
||||
};
|
@@ -1,21 +0,0 @@
|
||||
import { NodeDefinition } from "../schemas/types.ts";
|
||||
|
||||
type ExtractDefinitionMessage = {
|
||||
action: "extract-definition";
|
||||
content: ArrayBuffer;
|
||||
};
|
||||
|
||||
type ErrorMessage = {
|
||||
action: "error";
|
||||
error: Error;
|
||||
};
|
||||
|
||||
type ResultMessage = {
|
||||
action: "result";
|
||||
result: NodeDefinition;
|
||||
};
|
||||
|
||||
export type WorkerMessage =
|
||||
| ErrorMessage
|
||||
| ResultMessage
|
||||
| ExtractDefinitionMessage;
|
@@ -1,255 +0,0 @@
|
||||
// @ts-nocheck: Nocheck
|
||||
import { NodeDefinition } from "../schemas/types.ts";
|
||||
|
||||
const cachedTextDecoder = new TextDecoder("utf-8", {
|
||||
ignoreBOM: true,
|
||||
fatal: true,
|
||||
});
|
||||
const cachedTextEncoder = new TextEncoder();
|
||||
|
||||
const encodeString = typeof cachedTextEncoder.encodeInto === "function"
|
||||
? function (arg: string, view: Uint8Array) {
|
||||
return cachedTextEncoder.encodeInto(arg, view);
|
||||
}
|
||||
: function (arg: string, view: Uint8Array) {
|
||||
const buf = cachedTextEncoder.encode(arg);
|
||||
view.set(buf);
|
||||
return {
|
||||
read: arg.length,
|
||||
written: buf.length,
|
||||
};
|
||||
};
|
||||
|
||||
function createWrapper() {
|
||||
let wasm: WebAssembly.Exports & { memory: { buffer: Iterable<number> } };
|
||||
|
||||
let cachedUint8Memory0: Uint8Array | null = null;
|
||||
let cachedInt32Memory0: Int32Array | null = null;
|
||||
let cachedUint32Memory0: Uint32Array | null = null;
|
||||
|
||||
const heap = new Array(128).fill(undefined);
|
||||
heap.push(undefined, null, true, false);
|
||||
let heap_next = heap.length;
|
||||
|
||||
function getUint8Memory0() {
|
||||
if (cachedUint8Memory0 === null || cachedUint8Memory0.byteLength === 0) {
|
||||
cachedUint8Memory0 = new Uint8Array(wasm.memory.buffer);
|
||||
}
|
||||
return cachedUint8Memory0;
|
||||
}
|
||||
|
||||
function getInt32Memory0() {
|
||||
if (cachedInt32Memory0 === null || cachedInt32Memory0.byteLength === 0) {
|
||||
cachedInt32Memory0 = new Int32Array(wasm.memory.buffer);
|
||||
}
|
||||
return cachedInt32Memory0;
|
||||
}
|
||||
|
||||
function getUint32Memory0() {
|
||||
if (cachedUint32Memory0 === null || cachedUint32Memory0.byteLength === 0) {
|
||||
cachedUint32Memory0 = new Uint32Array(wasm.memory.buffer);
|
||||
}
|
||||
return cachedUint32Memory0;
|
||||
}
|
||||
|
||||
function getStringFromWasm0(ptr: number, len: number) {
|
||||
return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len));
|
||||
}
|
||||
|
||||
function getObject(idx: number) {
|
||||
return heap[idx];
|
||||
}
|
||||
|
||||
function addHeapObject(obj: unknown) {
|
||||
if (heap_next === heap.length) heap.push(heap.length + 1);
|
||||
const idx = heap_next;
|
||||
heap_next = heap[idx];
|
||||
heap[idx] = obj;
|
||||
return idx;
|
||||
}
|
||||
|
||||
let WASM_VECTOR_LEN = 0;
|
||||
function passArray32ToWasm0(
|
||||
arg: ArrayLike<number>,
|
||||
malloc: (arg0: number, arg1: number) => number,
|
||||
) {
|
||||
const ptr = malloc(arg.length * 4, 4) >>> 0;
|
||||
getUint32Memory0().set(arg, ptr / 4);
|
||||
WASM_VECTOR_LEN = arg.length;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
function getArrayI32FromWasm0(ptr: number, len: number) {
|
||||
ptr = ptr >>> 0;
|
||||
return getInt32Memory0().subarray(ptr / 4, ptr / 4 + len);
|
||||
}
|
||||
|
||||
function dropObject(idx: number) {
|
||||
if (idx < 132) return;
|
||||
heap[idx] = heap_next;
|
||||
heap_next = idx;
|
||||
}
|
||||
|
||||
function takeObject(idx: number) {
|
||||
const ret = getObject(idx);
|
||||
dropObject(idx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
function __wbindgen_string_new(arg0: number, arg1: number) {
|
||||
const ret = getStringFromWasm0(arg0, arg1);
|
||||
return addHeapObject(ret);
|
||||
}
|
||||
|
||||
// Additional methods and their internal helpers can also be refactored in a similar manner.
|
||||
function get_definition() {
|
||||
let deferred1_0: number;
|
||||
let deferred1_1: number;
|
||||
try {
|
||||
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
|
||||
wasm.get_definition(retptr);
|
||||
const r0 = getInt32Memory0()[retptr / 4 + 0];
|
||||
const r1 = getInt32Memory0()[retptr / 4 + 1];
|
||||
deferred1_0 = r0;
|
||||
deferred1_1 = r1;
|
||||
const rawDefinition = getStringFromWasm0(r0, r1);
|
||||
return JSON.parse(rawDefinition) as NodeDefinition;
|
||||
} finally {
|
||||
wasm.__wbindgen_add_to_stack_pointer(16);
|
||||
wasm.__wbindgen_free(deferred1_0, deferred1_1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
function execute(args: Int32Array) {
|
||||
try {
|
||||
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
|
||||
const ptr0 = passArray32ToWasm0(args, wasm.__wbindgen_malloc);
|
||||
const len0 = WASM_VECTOR_LEN;
|
||||
wasm.execute(retptr, ptr0, len0);
|
||||
const r0 = getInt32Memory0()[retptr / 4 + 0];
|
||||
const r1 = getInt32Memory0()[retptr / 4 + 1];
|
||||
const v2 = getArrayI32FromWasm0(r0, r1).slice();
|
||||
wasm.__wbindgen_free(r0, r1 * 4, 4);
|
||||
return v2;
|
||||
} finally {
|
||||
wasm.__wbindgen_add_to_stack_pointer(16);
|
||||
}
|
||||
}
|
||||
|
||||
function passStringToWasm0(
|
||||
arg: string,
|
||||
malloc: (arg0: number, arg1: number) => number,
|
||||
realloc:
|
||||
| ((arg0: number, arg1: number, arg2: number, arg3: number) => number)
|
||||
| undefined,
|
||||
) {
|
||||
if (realloc === undefined) {
|
||||
const buf = cachedTextEncoder.encode(arg);
|
||||
const ptr = malloc(buf.length, 1) >>> 0;
|
||||
getUint8Memory0()
|
||||
.subarray(ptr, ptr + buf.length)
|
||||
.set(buf);
|
||||
WASM_VECTOR_LEN = buf.length;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
let len = arg.length;
|
||||
let ptr = malloc(len, 1) >>> 0;
|
||||
|
||||
const mem = getUint8Memory0();
|
||||
|
||||
let offset = 0;
|
||||
|
||||
for (; offset < len; offset++) {
|
||||
const code = arg.charCodeAt(offset);
|
||||
if (code > 0x7f) break;
|
||||
mem[ptr + offset] = code;
|
||||
}
|
||||
|
||||
if (offset !== len) {
|
||||
if (offset !== 0) {
|
||||
arg = arg.slice(offset);
|
||||
}
|
||||
ptr = realloc(ptr, len, len = offset + arg.length * 3, 1) >>> 0;
|
||||
const view = getUint8Memory0().subarray(ptr + offset, ptr + len);
|
||||
const ret = encodeString(arg, view);
|
||||
|
||||
offset += ret.written;
|
||||
ptr = realloc(ptr, len, offset, 1) >>> 0;
|
||||
}
|
||||
|
||||
WASM_VECTOR_LEN = offset;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
function __wbg_new_abda76e883ba8a5f() {
|
||||
const ret = new Error();
|
||||
return addHeapObject(ret);
|
||||
}
|
||||
|
||||
function __wbg_stack_658279fe44541cf6(arg0: number, arg1: number) {
|
||||
const ret = getObject(arg1).stack;
|
||||
const ptr1 = passStringToWasm0(
|
||||
ret,
|
||||
wasm.__wbindgen_malloc,
|
||||
wasm.__wbindgen_realloc,
|
||||
);
|
||||
const len1 = WASM_VECTOR_LEN;
|
||||
getInt32Memory0()[arg0 / 4 + 1] = len1;
|
||||
getInt32Memory0()[arg0 / 4 + 0] = ptr1;
|
||||
}
|
||||
|
||||
function __wbg_error_f851667af71bcfc6(arg0: number, arg1: number) {
|
||||
let deferred0_0;
|
||||
let deferred0_1;
|
||||
try {
|
||||
deferred0_0 = arg0;
|
||||
deferred0_1 = arg1;
|
||||
console.error(getStringFromWasm0(arg0, arg1));
|
||||
} finally {
|
||||
wasm.__wbindgen_free(deferred0_0, deferred0_1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
function __wbindgen_object_drop_ref(arg0: number) {
|
||||
takeObject(arg0);
|
||||
}
|
||||
|
||||
function __wbg_log_5bb5f88f245d7762(arg0: number) {
|
||||
console.log(getObject(arg0));
|
||||
}
|
||||
|
||||
function __wbindgen_throw(arg0: number, arg1: number) {
|
||||
throw new Error(getStringFromWasm0(arg0, arg1));
|
||||
}
|
||||
|
||||
return {
|
||||
setInstance(instance: WebAssembly.Instance) {
|
||||
wasm = instance.exports;
|
||||
},
|
||||
|
||||
exports: {
|
||||
// Expose other methods that interact with the wasm instance
|
||||
execute,
|
||||
get_definition,
|
||||
},
|
||||
|
||||
__wbindgen_string_new,
|
||||
__wbindgen_object_drop_ref,
|
||||
__wbg_new_abda76e883ba8a5f,
|
||||
__wbg_error_f851667af71bcfc6,
|
||||
__wbg_stack_658279fe44541cf6,
|
||||
__wbg_log_5bb5f88f245d7762,
|
||||
__wbindgen_throw,
|
||||
};
|
||||
}
|
||||
|
||||
export function createWasmWrapper(wasmBuffer: ArrayBuffer) {
|
||||
const wrapper = createWrapper();
|
||||
const module = new WebAssembly.Module(wasmBuffer);
|
||||
const instance = new WebAssembly.Instance(module, {
|
||||
["./index_bg.js"]: wrapper,
|
||||
});
|
||||
wrapper.setInstance(instance);
|
||||
return wrapper.exports;
|
||||
}
|
@@ -1,10 +0,0 @@
|
||||
import { OpenAPIHono } from "@hono/zod-openapi";
|
||||
import { nodeRouter } from "./node/node.controller.ts";
|
||||
import { userRouter } from "./user/user.controller.ts";
|
||||
|
||||
const router = new OpenAPIHono();
|
||||
|
||||
router.route("nodes", nodeRouter);
|
||||
router.route("users", userRouter);
|
||||
|
||||
export { router };
|
@@ -1,54 +0,0 @@
|
||||
import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
|
||||
import { UserSchema, usersTable } from "./user.schema.ts";
|
||||
import { db } from "../../db/db.ts";
|
||||
import { findUserByName } from "./user.service.ts";
|
||||
|
||||
const userRouter = new OpenAPIHono();
|
||||
|
||||
const getAllUsersRoute = createRoute({
|
||||
method: "get",
|
||||
path: "/users.json",
|
||||
responses: {
|
||||
200: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: z.array(UserSchema),
|
||||
},
|
||||
},
|
||||
description: "Retrieve a single node definition",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
userRouter.openapi(getAllUsersRoute, async (c) => {
|
||||
const users = await db.select().from(usersTable);
|
||||
return c.json(users);
|
||||
});
|
||||
|
||||
const getSingleUserRoute = createRoute({
|
||||
method: "get",
|
||||
path: "/{userId}.json",
|
||||
request: {
|
||||
params: z.object({ userId: z.string().optional() }),
|
||||
},
|
||||
responses: {
|
||||
200: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: UserSchema,
|
||||
},
|
||||
},
|
||||
description: "Retrieve a single node definition",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
userRouter.openapi(getSingleUserRoute, async (c) => {
|
||||
const userId = c.req.param("userId.json");
|
||||
|
||||
const user = await findUserByName(userId.replace(/\.json$/, ""));
|
||||
|
||||
return c.json(user);
|
||||
});
|
||||
|
||||
export { userRouter };
|
@@ -1,14 +0,0 @@
|
||||
import { pgTable, text, uuid } from "drizzle-orm/pg-core";
|
||||
import { z } from "@hono/zod-openapi";
|
||||
|
||||
export const usersTable = pgTable("users", {
|
||||
id: uuid().primaryKey().defaultRandom(),
|
||||
name: text().unique().notNull(),
|
||||
});
|
||||
|
||||
export const UserSchema = z
|
||||
.object({
|
||||
id: z.string().uuid(),
|
||||
name: z.string().min(1), // Non-null text with a unique constraint (enforced at the database level)
|
||||
})
|
||||
.openapi("User");
|
@@ -1,28 +0,0 @@
|
||||
import { eq } from "drizzle-orm";
|
||||
import { db } from "../../db/db.ts";
|
||||
import { usersTable } from "./user.schema.ts";
|
||||
import * as uuid from "jsr:@std/uuid";
|
||||
|
||||
export async function createUser(userName: string) {
|
||||
const user = await db
|
||||
.select()
|
||||
.from(usersTable)
|
||||
.where(eq(usersTable.name, userName));
|
||||
|
||||
if (user.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
return await db
|
||||
.insert(usersTable)
|
||||
.values({ id: uuid.v1.generate(), name: userName });
|
||||
}
|
||||
|
||||
export async function findUserByName(userName: string) {
|
||||
const users = await db
|
||||
.select()
|
||||
.from(usersTable)
|
||||
.where(eq(usersTable.name, userName)).limit(1);
|
||||
|
||||
return users[0];
|
||||
}
|
@@ -1,7 +0,0 @@
|
||||
import { defineConfig } from "vitest/config";
|
||||
|
||||
export default defineConfig({
|
||||
test: {
|
||||
globals: true,
|
||||
},
|
||||
});
|
Reference in New Issue
Block a user