Compare commits

..

9 Commits

Author SHA1 Message Date
05b192e7ab commit to trigger deploy 2025-01-15 19:30:12 +01:00
edcaab4bd4 fix: use correct url 2025-01-15 18:17:04 +01:00
a99040f42e feat: some shit
Some checks failed
Deploy to GitHub Pages / build_site (push) Failing after 47s
2024-12-20 16:35:23 +01:00
fca59e87e5 feat: some shit 2024-12-20 16:35:16 +01:00
05e8970475 feat: use remote registry
All checks were successful
Deploy to GitHub Pages / build_site (push) Successful in 2m4s
2024-12-20 16:11:30 +01:00
385d1dd831 feat: use remote registry
All checks were successful
Deploy to GitHub Pages / build_site (push) Successful in 2m2s
2024-12-20 16:06:18 +01:00
dc46c4b64c feat: some stuff
All checks were successful
Deploy to GitHub Pages / build_site (push) Successful in 2m0s
2024-12-20 15:55:45 +01:00
15ff1cc52d feat: some shit
All checks were successful
Deploy to GitHub Pages / build_site (push) Successful in 2m4s
2024-12-20 15:24:54 +01:00
a70e8195a2 feat: add some more versining stuff 2024-12-20 14:06:33 +01:00
12 changed files with 523 additions and 539 deletions

View File

@ -32,7 +32,10 @@
let performanceStore = createPerformanceStore();
const registryCache = new IndexDBCache("node-registry");
const nodeRegistry = new RemoteNodeRegistry("", registryCache);
const nodeRegistry = new RemoteNodeRegistry(
"https://node-store.app.max-richter.dev",
registryCache,
);
const workerRuntime = new WorkerRuntimeExecutor();
const runtimeCache = new MemoryRuntimeCache();
const memoryRuntime = new MemoryRuntimeExecutor(nodeRegistry, runtimeCache);

View File

@ -45,6 +45,7 @@ async function postNode(node: Node) {
const wasmContent = await Deno.readFile(node.path);
const url = `http://localhost:8000/nodes`;
// const url = "https://node-store.app.max-richter.dev/nodes";
const res = await fetch(url, {
method: "POST",

View File

@ -7,12 +7,13 @@ CREATE TABLE "users" (
CREATE TABLE "nodes" (
"id" serial PRIMARY KEY NOT NULL,
"userId" varchar NOT NULL,
"createdAt" timestamp DEFAULT now(),
"systemId" varchar NOT NULL,
"nodeId" varchar NOT NULL,
"content" "bytea" NOT NULL,
"definition" json NOT NULL,
"hash" varchar(8) NOT NULL,
"previous" varchar(8),
"hash" varchar(16) NOT NULL,
"previous" varchar(16),
CONSTRAINT "nodes_hash_unique" UNIQUE("hash")
);
--> statement-breakpoint

View File

@ -1 +0,0 @@
ALTER TABLE "nodes" ADD COLUMN "createdAt" timestamp DEFAULT now();

View File

@ -1,5 +1,5 @@
{
"id": "b5fc8bcf-82d4-4d2e-bcd1-89d5a238f5e2",
"id": "15ad729d-5756-4c06-87ed-cb8b721201f9",
"prevId": "00000000-0000-0000-0000-000000000000",
"version": "7",
"dialect": "postgresql",
@ -54,6 +54,13 @@
"primaryKey": false,
"notNull": true
},
"createdAt": {
"name": "createdAt",
"type": "timestamp",
"primaryKey": false,
"notNull": false,
"default": "now()"
},
"systemId": {
"name": "systemId",
"type": "varchar",
@ -80,13 +87,13 @@
},
"hash": {
"name": "hash",
"type": "varchar(8)",
"type": "varchar(16)",
"primaryKey": false,
"notNull": true
},
"previous": {
"name": "previous",
"type": "varchar(8)",
"type": "varchar(16)",
"primaryKey": false,
"notNull": false
}

View File

@ -1,217 +0,0 @@
{
"id": "080ee514-5516-4400-9286-295826df6f8a",
"prevId": "b5fc8bcf-82d4-4d2e-bcd1-89d5a238f5e2",
"version": "7",
"dialect": "postgresql",
"tables": {
"public.users": {
"name": "users",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": true
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {
"users_name_unique": {
"name": "users_name_unique",
"nullsNotDistinct": false,
"columns": [
"name"
]
}
},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
},
"public.nodes": {
"name": "nodes",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "serial",
"primaryKey": true,
"notNull": true
},
"userId": {
"name": "userId",
"type": "varchar",
"primaryKey": false,
"notNull": true
},
"createdAt": {
"name": "createdAt",
"type": "timestamp",
"primaryKey": false,
"notNull": false,
"default": "now()"
},
"systemId": {
"name": "systemId",
"type": "varchar",
"primaryKey": false,
"notNull": true
},
"nodeId": {
"name": "nodeId",
"type": "varchar",
"primaryKey": false,
"notNull": true
},
"content": {
"name": "content",
"type": "bytea",
"primaryKey": false,
"notNull": true
},
"definition": {
"name": "definition",
"type": "json",
"primaryKey": false,
"notNull": true
},
"hash": {
"name": "hash",
"type": "varchar(8)",
"primaryKey": false,
"notNull": true
},
"previous": {
"name": "previous",
"type": "varchar(8)",
"primaryKey": false,
"notNull": false
}
},
"indexes": {
"user_id_idx": {
"name": "user_id_idx",
"columns": [
{
"expression": "userId",
"isExpression": false,
"asc": true,
"nulls": "last"
}
],
"isUnique": false,
"concurrently": false,
"method": "btree",
"with": {}
},
"system_id_idx": {
"name": "system_id_idx",
"columns": [
{
"expression": "systemId",
"isExpression": false,
"asc": true,
"nulls": "last"
}
],
"isUnique": false,
"concurrently": false,
"method": "btree",
"with": {}
},
"node_id_idx": {
"name": "node_id_idx",
"columns": [
{
"expression": "nodeId",
"isExpression": false,
"asc": true,
"nulls": "last"
}
],
"isUnique": false,
"concurrently": false,
"method": "btree",
"with": {}
},
"hash_idx": {
"name": "hash_idx",
"columns": [
{
"expression": "hash",
"isExpression": false,
"asc": true,
"nulls": "last"
}
],
"isUnique": false,
"concurrently": false,
"method": "btree",
"with": {}
}
},
"foreignKeys": {
"nodes_userId_users_name_fk": {
"name": "nodes_userId_users_name_fk",
"tableFrom": "nodes",
"tableTo": "users",
"columnsFrom": [
"userId"
],
"columnsTo": [
"name"
],
"onDelete": "no action",
"onUpdate": "no action"
},
"node_previous_fk": {
"name": "node_previous_fk",
"tableFrom": "nodes",
"tableTo": "nodes",
"columnsFrom": [
"previous"
],
"columnsTo": [
"hash"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {
"nodes_hash_unique": {
"name": "nodes_hash_unique",
"nullsNotDistinct": false,
"columns": [
"hash"
]
}
},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
}
},
"enums": {},
"schemas": {},
"sequences": {},
"roles": {},
"policies": {},
"views": {},
"_meta": {
"columns": {},
"schemas": {},
"tables": {}
}
}

View File

@ -5,15 +5,8 @@
{
"idx": 0,
"version": "7",
"when": 1734695353420,
"tag": "0000_steep_bromley",
"breakpoints": true
},
{
"idx": 1,
"version": "7",
"when": 1734696211359,
"tag": "0001_amazing_weapon_omega",
"when": 1734703963242,
"tag": "0000_known_kid_colt",
"breakpoints": true
}
]

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,33 @@
import { StatusCode } from "hono";
export class CustomError extends Error {
constructor(public status: StatusCode, message: string) {
super(message);
this.name = this.constructor.name;
Error.captureStackTrace(this, this.constructor);
}
}
export class NodeNotFoundError extends CustomError {
constructor() {
super(404, "Node not found");
}
}
export class InvalidNodeDefinitionError extends CustomError {
constructor() {
super(400, "Invalid node definition");
}
}
export class WorkerTimeoutError extends CustomError {
constructor() {
super(500, "Worker timed out");
}
}
export class UnknownWorkerResponseError extends CustomError {
constructor() {
super(500, "Unknown worker response");
}
}

View File

@ -3,238 +3,350 @@ import { HTTPException } from "hono/http-exception";
import { idRegex, NodeDefinitionSchema } from "./validations/types.ts";
import * as service from "./node.service.ts";
import { bodyLimit } from "hono/body-limit";
import { ZodSchema } from "zod";
import { CustomError } from "./errors.ts";
const nodeRouter = new OpenAPIHono();
const SingleParam = (name: string) =>
const createParamSchema = (name: string) =>
z
.string()
.min(3)
.max(20)
.refine(
(value) => idRegex.test(value),
`${name} should contain only letters, numbers, "-" or "_"`,
`${name} must contain only letters, numbers, "-", or "_"`,
)
.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 getNodeVersionRoute = createRoute({
method: "get",
path: "/{user}/{system}/{nodeId}@{hash}.json",
request: {
params: ParamsSchema,
},
responses: {
200: {
content: {
"application/json": {
schema: NodeDefinitionSchema,
},
},
description: "Create a single node",
},
const createResponseSchema = <T extends ZodSchema>(
description: string,
schema: T,
) => ({
200: {
content: { "application/json": { schema } },
description,
},
});
nodeRouter.openapi(getNodeVersionRoute, async (c) => {
const { user, system, nodeId } = c.req.valid("param");
const nodes = await service.getNodeVersions(user, system, nodeId);
return c.json(nodes);
});
const getNodeVersionsRoute = createRoute({
method: "get",
path: "/{user}/{system}/{nodeId}/versions.json",
request: {
params: z.object({
user: SingleParam("user"),
system: SingleParam("system"),
nodeId: SingleParam("nodeId"),
hash: SingleParam("hash"),
}),
},
responses: {
200: {
content: {
"application/json": {
schema: NodeDefinitionSchema,
},
},
description: "Create a single node",
},
},
});
nodeRouter.openapi(getNodeVersionsRoute, async (c) => {
const { user, system, nodeId, hash } = c.req.valid("param");
const node = await service.getNodeVersion(user, system, nodeId, hash);
return c.json(node);
});
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();
try {
const node = await service.createNode(buffer, bytes);
return c.json(node);
} catch (error) {
if (error instanceof Error && "code" in error) {
switch (error.code) {
case "23505":
throw new HTTPException(409, { message: "node already exists" });
async function getNodeByVersion(
user: string,
system: string,
nodeId: string,
hash?: string,
) {
console.log("Get Node by Version", { user, system, nodeId, hash });
if (hash) {
if (nodeId.includes("wasm")) {
return await service.getNodeVersionWasm(
user,
system,
nodeId.replace(".wasm", ""),
hash,
);
} else {
const wasmContent = await service.getNodeVersion(
user,
system,
nodeId,
hash,
);
return wasmContent;
}
} else {
if (nodeId.includes(".wasm")) {
const [id, version] = nodeId.replace(/\.wasm$/, "").split("@");
console.log({ user, system, id, version });
if (version) {
return service.getNodeVersionWasm(user, system, id, version);
} else {
return service.getNodeWasmById(user, system, id);
}
} else {
const [id, version] = nodeId.replace(/\.json$/, "").split("@");
if (!version) {
return service.getNodeDefinitionById(user, system, id);
} else {
return await service.getNodeVersion(user, system, id, version);
}
}
}
throw new HTTPException(500);
});
}
nodeRouter.openapi(
createRoute({
method: "post",
path: "/",
responses: createResponseSchema(
"Create a single node",
NodeDefinitionSchema,
),
middleware: [
bodyLimit({
maxSize: 128 * 1024, // 128 KB
onError: (c) => c.text("Node content too large", 413),
}),
],
}),
async (c) => {
const buffer = await c.req.arrayBuffer();
const bytes = new Uint8Array(buffer);
try {
const node = await service.createNode(buffer, bytes);
return c.json(node);
} catch (error) {
if (error instanceof CustomError) {
throw new HTTPException(error.status, { message: error.message });
}
throw new HTTPException(500, { message: "Internal server error" });
}
},
);
nodeRouter.openapi(
createRoute({
method: "get",
path: "/{user}.json",
request: {
params: z.object({
user: createParamSchema("user").optional(),
}),
},
responses: createResponseSchema(
"Retrieve nodes for a user",
z.array(NodeDefinitionSchema),
),
}),
async (c) => {
const user = c.req.param("user.json").replace(/\.json$/, "");
try {
const nodes = await service.getNodeDefinitionsByUser(user);
return c.json(nodes);
} catch (error) {
if (error instanceof CustomError) {
throw new HTTPException(error.status, { message: error.message });
}
throw new HTTPException(500, { message: "Internal server error" });
}
},
);
nodeRouter.openapi(
createRoute({
method: "get",
path: "/{user}/{system}.json",
request: {
params: z.object({
user: createParamSchema("user"),
system: createParamSchema("system").optional(),
}),
},
responses: createResponseSchema(
"Retrieve nodes for a system",
z.array(NodeDefinitionSchema),
),
}),
async (c) => {
const { user } = c.req.valid("param");
const system = c.req.param("system.json").replace(/\.json$/, "");
console.log("Get Nodes by System", { user, system });
try {
const nodes = await service.getNodesBySystem(user, system);
return c.json({
id: `${user}/${system}`,
nodes: nodes.map((n) => ({ id: n.id.split("@")[0] })),
});
} catch (error) {
if (error instanceof CustomError) {
throw new HTTPException(error.status, { message: error.message });
}
throw new HTTPException(500, { message: "Internal server error" });
}
},
);
nodeRouter.openapi(
createRoute({
method: "get",
path: "/{user}/{system}/{nodeId}.json",
request: {
params: z.object({
user: createParamSchema("user"),
system: createParamSchema("system"),
nodeId: createParamSchema("nodeId").optional(),
}),
},
responses: createResponseSchema(
"Retrieve a single node definition",
NodeDefinitionSchema,
),
}),
async (c) => {
const { user, system } = c.req.valid("param");
const nodeId = c.req.param("nodeId.json").replace(/\.json$/, "");
console.log("Get Node by Id", { user, system, nodeId });
try {
const res = await getNodeByVersion(user, system, nodeId);
if (res instanceof ArrayBuffer) {
c.header("Content-Type", "application/wasm");
return c.body(res);
} else {
return c.json(res);
}
} catch (error) {
if (error instanceof CustomError) {
throw new HTTPException(error.status, { message: error.message });
}
throw new HTTPException(500, { message: "Internal server error" });
}
},
);
nodeRouter.openapi(
createRoute({
method: "get",
path: "/{user}/{system}/{nodeId}@{version}.json",
request: {
params: z.object({
user: createParamSchema("user"),
system: createParamSchema("system"),
nodeId: createParamSchema("nodeId"),
version: createParamSchema("version").optional(),
}),
},
responses: createResponseSchema(
"Retrieve a single node definition",
NodeDefinitionSchema,
),
}),
async (c) => {
const { user, system, nodeId } = c.req.valid("param");
const hash = c.req.param("version.json");
try {
const res = await getNodeByVersion(user, system, nodeId, hash);
if (res instanceof ArrayBuffer) {
c.header("Content-Type", "application/wasm");
return c.body(res);
} else {
return c.json(res);
}
} catch (error) {
if (error instanceof CustomError) {
throw new HTTPException(error.status, { message: error.message });
}
throw new HTTPException(500, { message: "Internal server error" });
}
},
);
nodeRouter.openapi(
createRoute({
method: "get",
path: "/{user}/{system}/{nodeId}/versions.json",
request: {
params: z.object({
user: createParamSchema("user"),
system: createParamSchema("system"),
nodeId: createParamSchema("nodeId"),
}),
},
responses: createResponseSchema(
"Retrieve a single node definition",
z.array(NodeDefinitionSchema),
),
}),
async (c) => {
const { user, system, nodeId } = c.req.valid("param");
try {
const node = await service.getNodeVersions(user, system, nodeId);
return c.json(node);
} catch (error) {
if (error instanceof CustomError) {
throw new HTTPException(error.status, { message: error.message });
}
throw new HTTPException(500, { message: "Internal server error" });
}
},
);
nodeRouter.openapi(
createRoute({
method: "get",
path: "/{user}/{system}/{nodeId}.wasm",
request: {
params: z.object({
user: createParamSchema("user"),
system: createParamSchema("system"),
nodeId: createParamSchema("nodeId").optional(),
}),
},
responses: {
200: {
content: { "application/wasm": { schema: z.any() } },
description: "Retrieve a node's WASM file",
},
},
}),
async (c) => {
const { user, system } = c.req.valid("param");
const nodeId = c.req.param("nodeId.wasm");
console.log("Get NodeWasm by Id", { user, system, nodeId });
try {
const res = await getNodeByVersion(user, system, nodeId);
if (res instanceof ArrayBuffer) {
c.header("Content-Type", "application/wasm");
return c.body(res);
} else {
return c.json(res);
}
} catch (error) {
if (error instanceof CustomError) {
throw new HTTPException(error.status, { message: error.message });
}
throw new HTTPException(500, { message: "Internal server error" });
}
},
);
nodeRouter.openapi(
createRoute({
method: "get",
path: "/{user}/{system}/{nodeId}@{version}.wasm",
request: {
params: z.object({
user: createParamSchema("user"),
system: createParamSchema("system"),
nodeId: createParamSchema("nodeId"),
version: createParamSchema("version").optional(),
}),
},
responses: {
200: {
content: { "application/wasm": { schema: z.any() } },
description: "Retrieve a node's WASM file",
},
},
}),
async (c) => {
const { user, system, nodeId } = c.req.valid("param");
const hash = c.req.param("version.wasm");
try {
const res = await getNodeByVersion(user, system, nodeId, hash);
if (res instanceof ArrayBuffer) {
c.header("Content-Type", "application/wasm");
return c.body(res);
} else {
return c.json(res);
}
} catch (error) {
if (error instanceof CustomError) {
throw new HTTPException(error.status, { message: error.message });
}
throw new HTTPException(500, { message: "Internal server error" });
}
},
);
export { nodeRouter };

View File

@ -3,7 +3,8 @@ import { nodeTable } from "./node.schema.ts";
import { NodeDefinition, NodeDefinitionSchema } from "./validations/types.ts";
import { and, asc, eq } from "drizzle-orm";
import { createHash } from "node:crypto";
import { WorkerMessage } from "./worker/messages.ts";
import { extractDefinition } from "./worker/index.ts";
import { InvalidNodeDefinitionError, NodeNotFoundError } from "./errors.ts";
export type CreateNodeDTO = {
id: string;
@ -18,37 +19,6 @@ function getNodeHash(content: Uint8Array) {
return hash.digest("hex").slice(0, 16);
}
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":
rej(e.data.result);
break;
default:
rej(new Error("Unknown worker response"));
}
};
});
}
export async function createNode(
wasmBuffer: ArrayBuffer,
content: Uint8Array,
@ -83,16 +53,18 @@ export async function createNode(
}
export async function getNodeDefinitionsByUser(userName: string) {
const nodes = await db.select({ definition: nodeTable.definition }).from(
nodeTable,
)
.where(
and(
eq(nodeTable.userId, userName),
),
);
const nodes = await db
.select({
definition: nodeTable.definition,
hash: nodeTable.hash,
})
.from(nodeTable)
.where(and(eq(nodeTable.userId, userName)));
return nodes.map((n) => n.definition);
return nodes.map((n) => ({
...n.definition,
// id: n.definition.id + "@" + n.hash,
}));
}
export async function getNodesBySystem(
@ -102,17 +74,24 @@ export async function getNodesBySystem(
const nodes = await db
.selectDistinctOn(
[nodeTable.userId, nodeTable.systemId, nodeTable.nodeId],
{ definition: nodeTable.definition },
{ definition: nodeTable.definition, hash: nodeTable.hash },
)
.from(nodeTable)
.where(
and(eq(nodeTable.systemId, systemId), eq(nodeTable.userId, username)),
).orderBy(nodeTable.userId, nodeTable.systemId, nodeTable.nodeId);
)
.orderBy(nodeTable.userId, nodeTable.systemId, nodeTable.nodeId);
const definitions = nodes
.map((node) => NodeDefinitionSchema.safeParse(node.definition))
.filter((v) => v.success)
.map((v) => v.data);
.map(
(node) =>
[NodeDefinitionSchema.safeParse(node.definition), node.hash] as const,
)
.filter(([v]) => v.success)
.map(([v, hash]) => ({
...v.data,
// id: v?.data?.id + "@" + hash,
}));
return definitions;
}
@ -122,8 +101,9 @@ export async function getNodeWasmById(
systemId: string,
nodeId: string,
) {
const a = performance.now();
const node = await db.select({ content: nodeTable.content }).from(nodeTable)
const node = await db
.select({ content: nodeTable.content })
.from(nodeTable)
.where(
and(
eq(nodeTable.userId, userName),
@ -133,10 +113,9 @@ export async function getNodeWasmById(
)
.orderBy(asc(nodeTable.createdAt))
.limit(1);
console.log("Time to load wasm", performance.now() - a);
if (!node[0]) {
throw new Error("Node not found");
throw new NodeNotFoundError();
}
return node[0].content;
@ -147,29 +126,36 @@ export async function getNodeDefinitionById(
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),
),
)
const node = await db
.select({
definition: nodeTable.definition,
hash: nodeTable.hash,
})
.from(nodeTable)
.where(
and(
eq(nodeTable.userId, userName),
eq(nodeTable.systemId, systemId),
eq(nodeTable.nodeId, nodeId),
),
)
.orderBy(asc(nodeTable.createdAt))
.limit(1);
if (!node[0]) {
return;
throw new NodeNotFoundError();
}
const definition = NodeDefinitionSchema.safeParse(node[0]?.definition);
if (!definition.data) {
throw new Error("Invalid definition");
if (!definition.success) {
throw new InvalidNodeDefinitionError();
}
return definition.data;
return {
...definition.data,
// id: definition.data.id + "@" + node[0].hash
};
}
export async function getNodeVersions(
@ -177,23 +163,24 @@ export async function getNodeVersions(
system: string,
nodeId: string,
) {
const nodes = await db.select({
definition: nodeTable.definition,
hash: nodeTable.hash,
}).from(
nodeTable,
).where(
and(
eq(nodeTable.userId, user),
eq(nodeTable.systemId, system),
eq(nodeTable.nodeId, nodeId),
),
)
const nodes = await db
.select({
definition: nodeTable.definition,
hash: nodeTable.hash,
})
.from(nodeTable)
.where(
and(
eq(nodeTable.userId, user),
eq(nodeTable.systemId, system),
eq(nodeTable.nodeId, nodeId),
),
)
.orderBy(asc(nodeTable.createdAt));
return nodes.map((node) => ({
...node.definition,
id: node.definition.id + "@" + node.hash,
// id: node.definition.id + "@" + node.hash,
}));
}
@ -203,23 +190,52 @@ export async function getNodeVersion(
nodeId: string,
hash: string,
) {
const nodes = await db.select({
definition: nodeTable.definition,
hash: nodeTable.hash,
}).from(
nodeTable,
).where(
and(
eq(nodeTable.userId, user),
eq(nodeTable.systemId, system),
eq(nodeTable.nodeId, nodeId),
eq(nodeTable.hash, hash),
),
).limit(1);
const nodes = await db
.select({
definition: nodeTable.definition,
})
.from(nodeTable)
.where(
and(
eq(nodeTable.userId, user),
eq(nodeTable.systemId, system),
eq(nodeTable.nodeId, nodeId),
eq(nodeTable.hash, hash),
),
)
.limit(1);
if (nodes.length === 0) {
throw new Error("Node not found");
throw new NodeNotFoundError();
}
return nodes[0].definition;
}
export async function getNodeVersionWasm(
user: string,
system: string,
nodeId: string,
hash: string,
) {
const node = await db
.select({
content: nodeTable.content,
})
.from(nodeTable)
.where(
and(
eq(nodeTable.userId, user),
eq(nodeTable.systemId, system),
eq(nodeTable.nodeId, nodeId),
eq(nodeTable.hash, hash),
),
)
.limit(1);
if (node.length === 0) {
throw new NodeNotFoundError();
}
return node[0].content;
}

View File

@ -0,0 +1,36 @@
import { UnknownWorkerResponseError, WorkerTimeoutError } from "../errors.ts";
import { NodeDefinition } from "../validations/types.ts";
import { WorkerMessage } from "./messages.ts";
export function extractDefinition(
content: ArrayBuffer,
): Promise<NodeDefinition> {
const worker = new Worker(
new URL("./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 WorkerTimeoutError());
}, 100);
worker.onmessage = function (e) {
switch (e.data.action) {
case "result":
res(e.data.result);
break;
case "error":
rej(e.data.error);
break;
default:
rej(new UnknownWorkerResponseError());
}
};
});
}