diff --git a/app/src/lib/helpers/index.ts b/app/src/lib/helpers/index.ts index fb59137..7a1c866 100644 --- a/app/src/lib/helpers/index.ts +++ b/app/src/lib/helpers/index.ts @@ -65,7 +65,7 @@ export function createNodePath({ export const debounce = (fn: Function, ms = 300) => { let timeoutId: ReturnType; - return function(this: any, ...args: any[]) { + return function (this: any, ...args: any[]) { clearTimeout(timeoutId); timeoutId = setTimeout(() => fn.apply(this, args), ms); }; @@ -131,41 +131,100 @@ export function humanizeDuration(durationInMilliseconds: number) { return durationString.trim(); } -export function debounceAsyncFunction(func: (...args: T) => Promise): (...args: T) => Promise { - let currentPromise: Promise | null = null; - let nextArgs: T | null = null; - let resolveNext: ((result: R) => void) | null = null; +// export function debounceAsyncFunction( +// func: (...args: T) => Promise +// ): (...args: T) => Promise { +// let timeoutId: ReturnType | null = null; +// let lastPromise: Promise | null = null; +// let lastReject: ((reason?: any) => void) | null = null; +// +// return (...args: T): Promise => { +// if (timeoutId) { +// clearTimeout(timeoutId); +// if (lastReject) { +// lastReject(new Error("Debounced: Previous call was canceled.")); +// } +// } +// +// return new Promise((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 Promise>(asyncFn: T): T { + let isRunning = false; + let latestArgs: Parameters | null = null; + let resolveNext: (() => void) | null = null; - const debouncedFunction = async (...args: T): Promise => { - if (currentPromise) { - // Store the latest arguments and create a new promise to resolve them later - nextArgs = args; - return new Promise((resolve) => { + return (async function serializedFunction(...args: Parameters): Promise> { + latestArgs = args; + + if (isRunning) { + // Wait for the current execution to finish + await new Promise((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)); - } + } + + // 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; } } - }; - - return debouncedFunction; + }) as T; } +// export function debounceAsyncFunction(func: (...args: T) => Promise): (...args: T) => Promise { +// let currentPromise: Promise | null = null; +// let nextArgs: T | null = null; +// let resolveNext: ((result: R) => void) | null = null; +// +// const debouncedFunction = async (...args: T): Promise => { +// if (currentPromise) { +// // Store the latest arguments and create a new promise to resolve them later +// nextArgs = args; +// return new Promise((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; +// } + export function withArgsChangeOnly(func: (...args: T) => R): (...args: T) => R { let lastArgs: T | undefined = undefined; let lastResult: R; diff --git a/app/src/routes/+page.svelte b/app/src/routes/+page.svelte index 3fbc8fd..28ba236 100644 --- a/app/src/routes/+page.svelte +++ b/app/src/routes/+page.svelte @@ -34,7 +34,7 @@ let performanceStore = createPerformanceStore(); const registryCache = new IndexDBCache("node-registry"); - const nodeRegistry = new RemoteNodeRegistry(""); + const nodeRegistry = new RemoteNodeRegistry("http://localhost:8000/v1"); nodeRegistry.cache = registryCache; const workerRuntime = new WorkerRuntimeExecutor(); const runtimeCache = new MemoryRuntimeCache(); diff --git a/packages/registry/src/node-registry-client.ts b/packages/registry/src/node-registry-client.ts index f521aed..b3768d5 100644 --- a/packages/registry/src/node-registry-client.ts +++ b/packages/registry/src/node-registry-client.ts @@ -1,5 +1,5 @@ -import { type NodeRegistry, type NodeDefinition, NodeDefinitionSchema, type AsyncCache } from "@nodes/types"; -import { createWasmWrapper, createLogger } from "@nodes/utils"; +import { NodeDefinitionSchema, type AsyncCache, type NodeDefinition, type NodeRegistry } from "@nodes/types"; +import { createLogger, createWasmWrapper } from "@nodes/utils"; const log = createLogger("node-registry"); log.mute(); @@ -49,18 +49,21 @@ export class RemoteNodeRegistry implements NodeRegistry { private async fetchNodeWasm(nodeId: `${string}/${string}/${string}`) { - 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; - } - } + 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) { throw new Error(`Failed to load node wasm ${nodeId}`); } - return response.arrayBuffer(); + return res; } async load(nodeIds: `${string}/${string}/${string}`[]) { diff --git a/store/bin/upload.ts b/store/bin/upload.ts index 45b174c..fb2da96 100644 --- a/store/bin/upload.ts +++ b/store/bin/upload.ts @@ -52,8 +52,10 @@ async function postNode(node: Node) { }); if (res.ok) { - const json = await res.text(); - console.log(json); + console.log(`Uploaded ${node.id}`); + } else { + const text = await res.text(); + console.log(`Failed to upload ${node.id}: ${text}`); } } diff --git a/store/deno.json b/store/deno.json index 2de581b..0f3dd04 100644 --- a/store/deno.json +++ b/store/deno.json @@ -2,7 +2,7 @@ "tasks": { "dev": "deno run --watch main.ts", "test": "deno run vitest", - "drizzle": "docker compose exec app deno --env -A --node-modules-dir npm:drizzle-kit", + "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": { diff --git a/store/src/db/schema.ts b/store/src/db/schema.ts index b1953cc..d22ac0c 100644 --- a/store/src/db/schema.ts +++ b/store/src/db/schema.ts @@ -1,2 +1,2 @@ export * from "../routes/user/user.schema.ts"; -export * from "../routes/node/node.schema.ts"; +export * from "../routes/node/schemas/node.schema.ts"; diff --git a/store/src/main.ts b/store/src/main.ts index acdb02e..2b34e50 100644 --- a/store/src/main.ts +++ b/store/src/main.ts @@ -12,14 +12,14 @@ app.use("/v1/*", cors()); app.use(logger()); app.route("v1", router); -app.doc("/doc", { +app.doc("/openapi.json", { openapi: "3.0.0", info: { version: "1.0.0", - title: "My API", + title: "Nodarium API", }, }); -app.get("/ui", swaggerUI({ url: "/doc" })); +app.get("/ui", swaggerUI({ url: "/openapi.json" })); Deno.serve(app.fetch); diff --git a/store/src/routes/node/node.controller.ts b/store/src/routes/node/node.controller.ts index bf8b11f..8ac1dda 100644 --- a/store/src/routes/node/node.controller.ts +++ b/store/src/routes/node/node.controller.ts @@ -1,5 +1,6 @@ import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi"; -import { NodeDefinitionSchema } from "./types.ts"; +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"; @@ -11,24 +12,49 @@ const SingleParam = (name: string) => .min(3) .max(20) .refine( - (value) => /^[a-z_-]+$/i.test(value), + (value) => idRegex.test(value), "Name should contain only alphabets", ) .openapi({ param: { name, in: "path" } }); const ParamsSchema = z.object({ - userId: SingleParam("userId"), - nodeSystemId: SingleParam("nodeSystemId"), + 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: "/{userId}/{nodeSystemId}.json", + path: "/{user}/{system}.json", request: { params: z.object({ - userId: SingleParam("userId"), - nodeSystemId: SingleParam("nodeSystemId").optional(), + user: SingleParam("user"), + system: SingleParam("system").optional(), }), }, responses: { @@ -43,17 +69,16 @@ const getNodeCollectionRoute = createRoute({ }, }); nodeRouter.openapi(getNodeCollectionRoute, async (c) => { - const { userId } = c.req.valid("param"); - const nodeSystemId = c.req.param("nodeSystemId.json").replace(/\.json$/, ""); - - const nodes = await service.getNodesBySystem(userId, nodeSystemId); + 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: "/{userId}/{nodeSystemId}/{nodeId}.json", + path: "/{user}/{system}/{nodeId}{.+\\.json}", request: { params: ParamsSchema, }, @@ -68,15 +93,25 @@ const getNodeDefinitionRoute = createRoute({ }, }, }); -nodeRouter.openapi(getNodeDefinitionRoute, (c) => { - return c.json({ - id: "", - }); +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: "/{userId}/{nodeSystemId}/{nodeId}.wasm", + path: "/{user}/{system}/{nodeId}{.+\\.wasm}", request: { params: ParamsSchema, }, @@ -91,11 +126,18 @@ const getNodeWasmRoute = createRoute({ }, }, }); +nodeRouter.openapi(getNodeWasmRoute, async (c) => { + const { user, system, nodeId } = c.req.valid("param"); -nodeRouter.openapi(getNodeWasmRoute, (c) => { - return c.json({ - id: "", - }); + const wasmContent = await service.getNodeWasmById( + user, + system, + nodeId.replace(/\.wasm/, ""), + ); + + c.header("Content-Type", "application/wasm"); + + return c.body(wasmContent); }); const createNodeRoute = createRoute({ @@ -113,20 +155,17 @@ const createNodeRoute = createRoute({ }, middleware: [ bodyLimit({ - maxSize: 50 * 1024, // 50kb + maxSize: 128 * 1024, // 128kb onError: (c) => { - return c.text("overflow :(", 413); + 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); }); diff --git a/store/src/routes/node/node.service.ts b/store/src/routes/node/node.service.ts index beaf224..2deed45 100644 --- a/store/src/routes/node/node.service.ts +++ b/store/src/routes/node/node.service.ts @@ -1,7 +1,9 @@ import { db } from "../../db/db.ts"; -import { nodeTable } from "./node.schema.ts"; -import { NodeDefinition, NodeDefinitionSchema } from "./types.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; @@ -10,10 +12,21 @@ export type CreateNodeDTO = { 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 { - const worker = new Worker(new URL("./node.worker.ts", import.meta.url).href, { - type: "module", - }); + 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 }); @@ -22,12 +35,12 @@ function extractDefinition(content: ArrayBuffer): Promise { rej(new Error("Worker timeout out")); }, 100); worker.onmessage = function (e) { - console.log(e.data); 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: @@ -51,20 +64,28 @@ export async function createNode( systemId, nodeId, definition: def, + hash: getNodeHash(content), content: content, }; await db.insert(nodeTable).values(node); - console.log("New user created!"); - // await db.insert(users).values({ name: "Andrew" }); + console.log("new node created!"); return def; } catch (error) { - console.log({ error }); + console.log("Creation Error", { error }); throw error; } } -export async function getNodesByUser(userName: string) {} +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, @@ -84,4 +105,51 @@ export async function getNodesBySystem( return definitions; } -export async function getNodeById(dto: CreateNodeDTO) {} +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; +} diff --git a/store/src/routes/node/node.test.ts b/store/src/routes/node/node.test.ts index bb83f5e..bfd4522 100644 --- a/store/src/routes/node/node.test.ts +++ b/store/src/routes/node/node.test.ts @@ -4,7 +4,6 @@ import { router } from "../router.ts"; Deno.test("simple test", async () => { const res = await router.request("/max/plants/test.json"); const json = await res.text(); - console.log({ json }); expect(true).toEqual(true); diff --git a/store/src/routes/node/node.worker.ts b/store/src/routes/node/node.worker.ts deleted file mode 100644 index d88894b..0000000 --- a/store/src/routes/node/node.worker.ts +++ /dev/null @@ -1,30 +0,0 @@ -/// - -import { NodeDefinitionSchema } from "./types.ts"; -import { createWasmWrapper } from "./utils.ts"; - -function extractDefinition(wasmCode: ArrayBuffer) { - const wasm = createWasmWrapper(wasmCode); - - const definition = wasm.get_definition(); - - const p = NodeDefinitionSchema.safeParse(definition); - - if (!p.success) { - self.postMessage({ action: "error", error: p.error }); - return; - } - - self.postMessage({ action: "result", result: p.data }); -} - -self.onmessage = (e) => { - switch (e.data.action) { - case "extract-definition": - extractDefinition(e.data.content); - self.close(); - break; - default: - throw new Error("Unknwon action", e.data.action); - } -}; diff --git a/store/src/routes/node/inputs.ts b/store/src/routes/node/schemas/inputs.ts similarity index 81% rename from store/src/routes/node/inputs.ts rename to store/src/routes/node/schemas/inputs.ts index 25601f8..6e21773 100644 --- a/store/src/routes/node/inputs.ts +++ b/store/src/routes/node/schemas/inputs.ts @@ -10,7 +10,7 @@ const DefaultOptionsSchema = z.object({ hidden: z.boolean().optional(), }); -export const NodeInputFloatSchema = z.object({ +const NodeInputFloatSchema = z.object({ ...DefaultOptionsSchema.shape, type: z.literal("float"), element: z.literal("slider").optional(), @@ -20,7 +20,7 @@ export const NodeInputFloatSchema = z.object({ step: z.number().optional(), }); -export const NodeInputIntegerSchema = z.object({ +const NodeInputIntegerSchema = z.object({ ...DefaultOptionsSchema.shape, type: z.literal("integer"), element: z.literal("slider").optional(), @@ -29,37 +29,37 @@ export const NodeInputIntegerSchema = z.object({ max: z.number().optional(), }); -export const NodeInputBooleanSchema = z.object({ +const NodeInputBooleanSchema = z.object({ ...DefaultOptionsSchema.shape, type: z.literal("boolean"), value: z.boolean().optional(), }); -export const NodeInputSelectSchema = z.object({ +const NodeInputSelectSchema = z.object({ ...DefaultOptionsSchema.shape, type: z.literal("select"), options: z.array(z.string()).optional(), value: z.number().optional(), }); -export const NodeInputSeedSchema = z.object({ +const NodeInputSeedSchema = z.object({ ...DefaultOptionsSchema.shape, type: z.literal("seed"), value: z.number().optional(), }); -export const NodeInputVec3Schema = z.object({ +const NodeInputVec3Schema = z.object({ ...DefaultOptionsSchema.shape, type: z.literal("vec3"), value: z.array(z.number()).optional(), }); -export const NodeInputGeometrySchema = z.object({ +const NodeInputGeometrySchema = z.object({ ...DefaultOptionsSchema.shape, type: z.literal("geometry"), }); -export const NodeInputPathSchema = z.object({ +const NodeInputPathSchema = z.object({ ...DefaultOptionsSchema.shape, type: z.literal("path"), }); diff --git a/store/src/routes/node/node.schema.ts b/store/src/routes/node/schemas/node.schema.ts similarity index 89% rename from store/src/routes/node/node.schema.ts rename to store/src/routes/node/schemas/node.schema.ts index 971ef63..bb67512 100644 --- a/store/src/routes/node/node.schema.ts +++ b/store/src/routes/node/schemas/node.schema.ts @@ -7,7 +7,7 @@ import { varchar, } from "drizzle-orm/pg-core"; import { relations } from "drizzle-orm/relations"; -import { usersTable } from "../user/user.schema.ts"; +import { usersTable } from "../../user/user.schema.ts"; const bytea = customType<{ data: ArrayBuffer; @@ -25,6 +25,7 @@ export const nodeTable = pgTable("nodes", { nodeId: varchar().notNull(), content: bytea().notNull(), definition: json().notNull(), + hash: varchar({ length: 8 }).notNull(), previous: integer(), }); diff --git a/store/src/routes/node/schemas/types.ts b/store/src/routes/node/schemas/types.ts new file mode 100644 index 0000000..3281e15 --- /dev/null +++ b/store/src/routes/node/schemas/types.ts @@ -0,0 +1,31 @@ +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; diff --git a/store/src/routes/node/types.ts b/store/src/routes/node/types.ts deleted file mode 100644 index 3274cfc..0000000 --- a/store/src/routes/node/types.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { z } from "zod"; -import { NodeInputSchema } from "./inputs.ts"; - -export type NodeId = `${string}/${string}/${string}`; - -export const NodeSchema = z.object({ - id: z.number(), - type: z.string(), - props: z.record(z.union([z.number(), z.array(z.number())])).optional(), - meta: z - .object({ - title: z.string().optional(), - lastModified: z.string().optional(), - }) - .optional(), - position: z.tuple([z.number(), z.number()]), -}); - -export type Node = z.infer; - -const partPattern = /[a-z-_]{3,32}/; - -const idSchema = z - .string() - .regex( - new RegExp( - `^(${partPattern.source})/(${partPattern.source})/(${partPattern.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; - -export type Socket = { - node: Node; - index: number | string; - position: [number, number]; -}; - -export type Edge = [Node, number, Node, string]; - -export const GraphSchema = z.object({ - id: z.number().optional(), - meta: z - .object({ - title: z.string().optional(), - lastModified: z.string().optional(), - }) - .optional(), - settings: z.record(z.any()).optional(), - nodes: z.array(NodeSchema), - edges: z.array(z.tuple([z.number(), z.number(), z.number(), z.string()])), -}); - -export type Graph = z.infer & { nodes: Node[] }; diff --git a/store/src/routes/node/worker/node.worker.ts b/store/src/routes/node/worker/node.worker.ts new file mode 100644 index 0000000..3247983 --- /dev/null +++ b/store/src/routes/node/worker/node.worker.ts @@ -0,0 +1,40 @@ +/// + +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) => { + switch (e.data.action) { + case "extract-definition": + extractDefinition(e.data.content); + self.close(); + break; + default: + throw new Error("Unknown action: " + e.data.action); + } +}; diff --git a/store/src/routes/node/worker/types.ts b/store/src/routes/node/worker/types.ts new file mode 100644 index 0000000..6c1f6f1 --- /dev/null +++ b/store/src/routes/node/worker/types.ts @@ -0,0 +1,21 @@ +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; diff --git a/store/src/routes/node/utils.ts b/store/src/routes/node/worker/utils.ts similarity index 99% rename from store/src/routes/node/utils.ts rename to store/src/routes/node/worker/utils.ts index 65876dd..4ff334e 100644 --- a/store/src/routes/node/utils.ts +++ b/store/src/routes/node/worker/utils.ts @@ -1,5 +1,5 @@ // @ts-nocheck: Nocheck -import { NodeDefinition } from "./types.ts"; +import { NodeDefinition } from "../schemas/types.ts"; const cachedTextDecoder = new TextDecoder("utf-8", { ignoreBOM: true, diff --git a/store/src/routes/router.ts b/store/src/routes/router.ts index 86f392a..10442fb 100644 --- a/store/src/routes/router.ts +++ b/store/src/routes/router.ts @@ -5,6 +5,6 @@ import { userRouter } from "./user/user.controller.ts"; const router = new OpenAPIHono(); router.route("nodes", nodeRouter); -router.route("nodes", userRouter); +router.route("users", userRouter); export { router }; diff --git a/store/src/routes/user/user.service.ts b/store/src/routes/user/user.service.ts index 094cfee..55fe6df 100644 --- a/store/src/routes/user/user.service.ts +++ b/store/src/routes/user/user.service.ts @@ -22,7 +22,7 @@ export async function findUserByName(userName: string) { const users = await db .select() .from(usersTable) - .where(eq(usersTable.name, userName)); + .where(eq(usersTable.name, userName)).limit(1); return users[0]; }