feat: deno-first storage package with sqlite host and graph schemas
Scaffolded @alkdev/storage from @ade/storage_sqlite and @ade/core/graphs: - graphs/ module: TypeBox schema types + SchemaBuilder (from @ade/core/graphs) - sqlite/ module: Drizzle table defs, relations, injectable client (from @ade/storage_sqlite) - pg/ module: placeholder for Postgres host - deno.json configured for JSR with subpath exports (./graphs, ./sqlite, ./pg) - Imports swapped: @sinclair/typebox → @alkdev/typebox, drizzle-typebox → @alkdev/drizzlebox - Client is now injectable (no hardcoded env vars or module-level side effects) - no-slow-types lint excluded (Drizzle generics); --allow-slow-types on publish
This commit is contained in:
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
node_modules/
|
||||||
|
deno.lock
|
||||||
|
.npmrc
|
||||||
33
deno.json
Normal file
33
deno.json
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"name": "@alkdev/storage",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"license": "MIT",
|
||||||
|
"exports": {
|
||||||
|
".": "./mod.ts",
|
||||||
|
"./graphs": "./src/graphs/mod.ts",
|
||||||
|
"./sqlite": "./src/sqlite/mod.ts",
|
||||||
|
"./pg": "./src/pg/mod.ts"
|
||||||
|
},
|
||||||
|
"imports": {
|
||||||
|
"@alkdev/typebox": "npm:@alkdev/typebox",
|
||||||
|
"@alkdev/drizzlebox": "npm:@alkdev/drizzlebox",
|
||||||
|
"drizzle-orm": "npm:drizzle-orm",
|
||||||
|
"drizzle-orm/sqlite-core": "npm:drizzle-orm/sqlite-core",
|
||||||
|
"drizzle-orm/pg-core": "npm:drizzle-orm/pg-core",
|
||||||
|
"@libsql/client": "npm:@libsql/client",
|
||||||
|
"postgres": "npm:postgres",
|
||||||
|
"@std/assert": "jsr:@std/assert"
|
||||||
|
},
|
||||||
|
"lint": {
|
||||||
|
"rules": {
|
||||||
|
"exclude": ["no-slow-types"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tasks": {
|
||||||
|
"check": "deno check mod.ts src/graphs/mod.ts src/sqlite/mod.ts",
|
||||||
|
"test": "deno test --allow-all test/",
|
||||||
|
"lint": "deno lint",
|
||||||
|
"fmt": "deno fmt",
|
||||||
|
"publish:dry": "deno publish --allow-slow-types --dry-run"
|
||||||
|
}
|
||||||
|
}
|
||||||
2
src/graphs/mod.ts
Normal file
2
src/graphs/mod.ts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
export * from "./types.ts";
|
||||||
|
export * from "./schemaBuilder.ts";
|
||||||
63
src/graphs/schemaBuilder.ts
Normal file
63
src/graphs/schemaBuilder.ts
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
import { KindGuard, type TSchema } from "@alkdev/typebox";
|
||||||
|
import { Value } from "@alkdev/typebox/value";
|
||||||
|
import { assert } from "@std/assert";
|
||||||
|
import { GraphSchema, GraphConfig, NodeType, EdgeType } from "./types.ts";
|
||||||
|
|
||||||
|
export class SchemaBuilder {
|
||||||
|
private schema: {
|
||||||
|
config: Record<string, unknown>;
|
||||||
|
nodeTypes: Record<string, NodeType>;
|
||||||
|
edgeTypes: Record<string, EdgeType>;
|
||||||
|
} = {
|
||||||
|
config: {},
|
||||||
|
nodeTypes: {},
|
||||||
|
edgeTypes: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
config(config: Partial<GraphConfig>): SchemaBuilder {
|
||||||
|
const configObj = Value.Default(GraphConfig, config) as GraphConfig;
|
||||||
|
this.check(GraphConfig, configObj);
|
||||||
|
this.schema.config = configObj as Record<string, unknown>;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
nodeType(name: string, schema: TSchema): SchemaBuilder {
|
||||||
|
assert(KindGuard.IsSchema(schema), `type '${name}' is not a valid json schema.`);
|
||||||
|
|
||||||
|
if (!this.schema.nodeTypes) this.schema.nodeTypes = {};
|
||||||
|
const nodeTypeObj: NodeType = { name, schema };
|
||||||
|
|
||||||
|
this.check(NodeType, nodeTypeObj);
|
||||||
|
this.schema.nodeTypes[name] = nodeTypeObj;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
edgeType(
|
||||||
|
name: string,
|
||||||
|
schema: TSchema,
|
||||||
|
options?: { allowedSourceTypes?: string[]; allowedTargetTypes?: string[] },
|
||||||
|
): SchemaBuilder {
|
||||||
|
assert(KindGuard.IsSchema(schema), `type '${name}' is not a valid json schema.`);
|
||||||
|
|
||||||
|
if (!this.schema.edgeTypes) this.schema.edgeTypes = {};
|
||||||
|
const edgeTypeObj: EdgeType = { name, schema, ...options };
|
||||||
|
|
||||||
|
this.check(EdgeType, edgeTypeObj);
|
||||||
|
this.schema.edgeTypes[name] = edgeTypeObj;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
check(schema: TSchema, value: unknown): void {
|
||||||
|
if (!Value.Check(schema, value)) {
|
||||||
|
const errors = [...Value.Errors(schema, value)];
|
||||||
|
throw new Error(
|
||||||
|
`Invalid schema structure: ${JSON.stringify(errors.map((e) => `${e.path}: ${e.message}`))}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
build(): GraphSchema {
|
||||||
|
this.check(GraphSchema, this.schema);
|
||||||
|
return this.schema as GraphSchema;
|
||||||
|
}
|
||||||
|
}
|
||||||
70
src/graphs/types.ts
Normal file
70
src/graphs/types.ts
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
import { Type, type Static, type TSchema } from "@alkdev/typebox";
|
||||||
|
|
||||||
|
export const BaseNodeAttributes: TSchema = Type.Object({
|
||||||
|
created: Type.Optional(Type.String({ format: "date-time" })),
|
||||||
|
modified: Type.Optional(Type.String({ format: "date-time" })),
|
||||||
|
metadata: Type.Optional(Type.Record(Type.String(), Type.Any())),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type BaseNodeAttributes = Static<typeof BaseNodeAttributes>;
|
||||||
|
|
||||||
|
export const BaseEdgeAttributes: TSchema = Type.Object({
|
||||||
|
type: Type.String(),
|
||||||
|
metadata: Type.Optional(Type.Record(Type.String(), Type.Any())),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type BaseEdgeAttributes = Static<typeof BaseEdgeAttributes>;
|
||||||
|
|
||||||
|
export const GraphConfig: TSchema = Type.Object({
|
||||||
|
type: Type.Union([
|
||||||
|
Type.Literal("directed"),
|
||||||
|
Type.Literal("undirected"),
|
||||||
|
Type.Literal("mixed"),
|
||||||
|
], { default: "mixed" }),
|
||||||
|
multi: Type.Boolean({ default: true }),
|
||||||
|
allowSelfLoops: Type.Boolean({ default: true }),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type GraphConfig = Static<typeof GraphConfig>;
|
||||||
|
|
||||||
|
export const NodeType: TSchema = Type.Object({
|
||||||
|
name: Type.String(),
|
||||||
|
schema: Type.Any(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type NodeType = Static<typeof NodeType>;
|
||||||
|
|
||||||
|
export const EdgeType: TSchema = Type.Object({
|
||||||
|
name: Type.String(),
|
||||||
|
schema: Type.Any(),
|
||||||
|
allowedSourceTypes: Type.Optional(Type.Array(Type.String())),
|
||||||
|
allowedTargetTypes: Type.Optional(Type.Array(Type.String())),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type EdgeType = Static<typeof EdgeType>;
|
||||||
|
|
||||||
|
export const GraphSchema: TSchema = Type.Object({
|
||||||
|
config: GraphConfig,
|
||||||
|
nodeTypes: Type.Record(Type.String(), NodeType),
|
||||||
|
edgeTypes: Type.Record(Type.String(), EdgeType),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type GraphSchema = Static<typeof GraphSchema>;
|
||||||
|
|
||||||
|
export enum EnumGraphStatus {
|
||||||
|
Active = "active",
|
||||||
|
Archived = "archived",
|
||||||
|
Draft = "draft",
|
||||||
|
}
|
||||||
|
|
||||||
|
export type GraphStatus = Static<typeof GraphStatus>;
|
||||||
|
export const GraphStatus: TSchema = Type.Enum(EnumGraphStatus);
|
||||||
|
|
||||||
|
export enum EnumGraphBaseType {
|
||||||
|
Directed = "directed",
|
||||||
|
Undirected = "undirected",
|
||||||
|
Mixed = "mixed",
|
||||||
|
}
|
||||||
|
|
||||||
|
export type GraphBaseType = Static<typeof GraphBaseType>;
|
||||||
|
export const GraphBaseType: TSchema = Type.Enum(EnumGraphBaseType);
|
||||||
3
src/pg/mod.ts
Normal file
3
src/pg/mod.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
// Postgres host — not yet implemented
|
||||||
|
// Will mirror sqlite/ structure with pgTable, jsonb(), timestamp(), pgEnum, etc.
|
||||||
|
// Import pattern: import { ... } from "@alkdev/storage/pg"
|
||||||
11
src/sqlite/client.ts
Normal file
11
src/sqlite/client.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import { drizzle, type LibSQLDatabase } from "drizzle-orm/libsql";
|
||||||
|
import type { Client } from "@libsql/client";
|
||||||
|
import * as schema from "./schema.ts";
|
||||||
|
|
||||||
|
export type SqliteSchema = typeof schema;
|
||||||
|
|
||||||
|
export type SqliteDatabase = LibSQLDatabase<SqliteSchema>;
|
||||||
|
|
||||||
|
export function createSqliteDatabase(client: Client): SqliteDatabase {
|
||||||
|
return drizzle(client, { schema }) as SqliteDatabase;
|
||||||
|
}
|
||||||
2
src/sqlite/mod.ts
Normal file
2
src/sqlite/mod.ts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
export * from "./schema.ts";
|
||||||
|
export * from "./client.ts";
|
||||||
68
src/sqlite/relations.ts
Normal file
68
src/sqlite/relations.ts
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
import { relations } from "drizzle-orm";
|
||||||
|
import {
|
||||||
|
graphTypes,
|
||||||
|
nodeTypes,
|
||||||
|
edgeTypes,
|
||||||
|
graphs,
|
||||||
|
nodes,
|
||||||
|
edges,
|
||||||
|
} from "./tables/index.ts";
|
||||||
|
|
||||||
|
export const graphTypeRelations = relations(graphTypes, ({ many }) => ({
|
||||||
|
nodeTypes: many(nodeTypes),
|
||||||
|
edgeTypes: many(edgeTypes),
|
||||||
|
graphs: many(graphs),
|
||||||
|
}));
|
||||||
|
|
||||||
|
export const nodeTypesRelations = relations(nodeTypes, ({ one }) => ({
|
||||||
|
graphType: one(graphTypes, {
|
||||||
|
fields: [nodeTypes.graphTypeId],
|
||||||
|
references: [graphTypes.id],
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
export const edgeTypesRelations = relations(edgeTypes, ({ one }) => ({
|
||||||
|
graphType: one(graphTypes, {
|
||||||
|
fields: [edgeTypes.graphTypeId],
|
||||||
|
references: [graphTypes.id],
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
export const graphsRelations = relations(graphs, ({ one, many }) => ({
|
||||||
|
graphType: one(graphTypes, {
|
||||||
|
fields: [graphs.graphTypeId],
|
||||||
|
references: [graphTypes.id],
|
||||||
|
}),
|
||||||
|
nodes: many(nodes),
|
||||||
|
edges: many(edges),
|
||||||
|
}));
|
||||||
|
|
||||||
|
export const edgesRelations = relations(edges, ({ one }) => ({
|
||||||
|
graph: one(graphs, {
|
||||||
|
fields: [edges.graphId],
|
||||||
|
references: [graphs.id],
|
||||||
|
}),
|
||||||
|
sourceNode: one(nodes, {
|
||||||
|
fields: [edges.graphId, edges.sourceNodeKey],
|
||||||
|
references: [nodes.graphId, nodes.key],
|
||||||
|
relationName: "sourceNode",
|
||||||
|
}),
|
||||||
|
targetNode: one(nodes, {
|
||||||
|
fields: [edges.graphId, edges.targetNodeKey],
|
||||||
|
references: [nodes.graphId, nodes.key],
|
||||||
|
relationName: "targetNode",
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
export const nodesRelations = relations(nodes, ({ one, many }) => ({
|
||||||
|
graph: one(graphs, {
|
||||||
|
fields: [nodes.graphId],
|
||||||
|
references: [graphs.id],
|
||||||
|
}),
|
||||||
|
outgoingEdges: many(edges, {
|
||||||
|
relationName: "sourceNode",
|
||||||
|
}),
|
||||||
|
incomingEdges: many(edges, {
|
||||||
|
relationName: "targetNode",
|
||||||
|
}),
|
||||||
|
}));
|
||||||
2
src/sqlite/schema.ts
Normal file
2
src/sqlite/schema.ts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
export * from "./tables/index.ts";
|
||||||
|
export * from "./relations.ts";
|
||||||
30
src/sqlite/tables/actors.ts
Normal file
30
src/sqlite/tables/actors.ts
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
import { sqliteTable, text } from "drizzle-orm/sqlite-core";
|
||||||
|
import { createInsertSchema, createSelectSchema } from "@alkdev/drizzlebox";
|
||||||
|
import { Type, type Static } from "@alkdev/typebox";
|
||||||
|
import { commonCols, ACTOR_TYPE } from "./common.ts";
|
||||||
|
|
||||||
|
export const actors = sqliteTable("actors", {
|
||||||
|
...commonCols,
|
||||||
|
name: text("name").notNull(),
|
||||||
|
type: text("type", { enum: ["human", "llm", "agent"] }).notNull(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const SelectActor = createSelectSchema(actors, {
|
||||||
|
metadata: Type.Object({}, { additionalProperties: true }),
|
||||||
|
createdAt: Type.Date(),
|
||||||
|
updatedAt: Type.Date(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type SelectActor = Static<typeof SelectActor>;
|
||||||
|
|
||||||
|
export const InsertActor = createInsertSchema(actors, {
|
||||||
|
name: Type.String({ minLength: 1, maxLength: 255 }),
|
||||||
|
type: Type.Union([
|
||||||
|
Type.Literal(ACTOR_TYPE.Human),
|
||||||
|
Type.Literal(ACTOR_TYPE.Llm),
|
||||||
|
Type.Literal(ACTOR_TYPE.Agent),
|
||||||
|
]),
|
||||||
|
metadata: Type.Optional(Type.Object({}, { additionalProperties: true })),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type InsertActor = Static<typeof InsertActor>;
|
||||||
21
src/sqlite/tables/common.ts
Normal file
21
src/sqlite/tables/common.ts
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import { sql } from "drizzle-orm";
|
||||||
|
import { text, integer } from "drizzle-orm/sqlite-core";
|
||||||
|
|
||||||
|
export const commonCols = {
|
||||||
|
id: text("id").primaryKey(),
|
||||||
|
metadata: text("metadata", { mode: "json" }).$type<Record<string, unknown>>().default({}),
|
||||||
|
createdAt: integer("created_at", { mode: "timestamp" })
|
||||||
|
.default(sql`(strftime('%s', 'now'))`)
|
||||||
|
.notNull(),
|
||||||
|
updatedAt: integer("updated_at", { mode: "timestamp" })
|
||||||
|
.default(sql`(strftime('%s', 'now'))`)
|
||||||
|
.notNull(),
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ACTOR_TYPE = {
|
||||||
|
Human: "human",
|
||||||
|
Llm: "llm",
|
||||||
|
Agent: "agent",
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export type EnumValues<T extends Record<string, string>> = T[keyof T];
|
||||||
37
src/sqlite/tables/edgeTypes.ts
Normal file
37
src/sqlite/tables/edgeTypes.ts
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import { sqliteTable, text, unique } from "drizzle-orm/sqlite-core";
|
||||||
|
import { createInsertSchema, createSelectSchema } from "@alkdev/drizzlebox";
|
||||||
|
import { Type, type Static } from "@alkdev/typebox";
|
||||||
|
import { commonCols } from "./common.ts";
|
||||||
|
import { graphTypes } from "./graphTypes.ts";
|
||||||
|
|
||||||
|
export const edgeTypes = sqliteTable("edge_types", {
|
||||||
|
...commonCols,
|
||||||
|
graphTypeId: text("graph_type_id").notNull().references(() => graphTypes.id, { onDelete: "cascade" }),
|
||||||
|
name: text("name").notNull(),
|
||||||
|
description: text("description").default(""),
|
||||||
|
schema: text("schema", { mode: "json" }).$type<Record<string, unknown>>().notNull(),
|
||||||
|
allowedSourceTypes: text("allowed_source_types", { mode: "json" }).$type<string[]>().default([]),
|
||||||
|
allowedTargetTypes: text("allowed_target_types", { mode: "json" }).$type<string[]>().default([]),
|
||||||
|
}, (table) => ({
|
||||||
|
graphTypeNameIdx: unique().on(table.graphTypeId, table.name),
|
||||||
|
}));
|
||||||
|
|
||||||
|
export const SelectEdgeType = createSelectSchema(edgeTypes, {
|
||||||
|
schema: Type.Unknown(),
|
||||||
|
allowedSourceTypes: Type.Array(Type.String()),
|
||||||
|
allowedTargetTypes: Type.Array(Type.String()),
|
||||||
|
metadata: Type.Object({}, { additionalProperties: true }),
|
||||||
|
createdAt: Type.Date(),
|
||||||
|
updatedAt: Type.Date(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type SelectEdgeType = Static<typeof SelectEdgeType>;
|
||||||
|
|
||||||
|
export const InsertEdgeType = createInsertSchema(edgeTypes, {
|
||||||
|
name: Type.String({ minLength: 2 }),
|
||||||
|
schema: Type.Unknown(),
|
||||||
|
allowedSourceTypes: Type.Optional(Type.Array(Type.String())),
|
||||||
|
allowedTargetTypes: Type.Optional(Type.Array(Type.String())),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type InsertEdgeType = Static<typeof InsertEdgeType>;
|
||||||
44
src/sqlite/tables/edges.ts
Normal file
44
src/sqlite/tables/edges.ts
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
import { sqliteTable, text, integer, unique, foreignKey } from "drizzle-orm/sqlite-core";
|
||||||
|
import { createInsertSchema, createSelectSchema } from "@alkdev/drizzlebox";
|
||||||
|
import { Type, type Static } from "@alkdev/typebox";
|
||||||
|
import { commonCols } from "./common.ts";
|
||||||
|
import { graphs } from "./graphs.ts";
|
||||||
|
import { nodes } from "./nodes.ts";
|
||||||
|
|
||||||
|
const AttributesSchema = Type.Record(Type.String(), Type.Any());
|
||||||
|
|
||||||
|
export const edges = sqliteTable("edges", {
|
||||||
|
...commonCols,
|
||||||
|
graphId: text("graph_id").notNull().references(() => graphs.id, { onDelete: "cascade" }),
|
||||||
|
key: text("key"),
|
||||||
|
sourceNodeKey: text("source_node_key").notNull(),
|
||||||
|
targetNodeKey: text("target_node_key").notNull(),
|
||||||
|
attributes: text("attributes", { mode: "json" }).notNull().default({}),
|
||||||
|
undirected: integer("undirected", { mode: "boolean" }).default(false),
|
||||||
|
}, (table) => ({
|
||||||
|
graphKeyIdx: unique().on(table.graphId, table.key),
|
||||||
|
sourceFk: foreignKey({
|
||||||
|
columns: [table.graphId, table.sourceNodeKey],
|
||||||
|
foreignColumns: [nodes.graphId, nodes.key],
|
||||||
|
}).onDelete("cascade"),
|
||||||
|
targetFk: foreignKey({
|
||||||
|
columns: [table.graphId, table.targetNodeKey],
|
||||||
|
foreignColumns: [nodes.graphId, nodes.key],
|
||||||
|
}).onDelete("cascade"),
|
||||||
|
}));
|
||||||
|
|
||||||
|
export const SelectEdge = createSelectSchema(edges, {
|
||||||
|
attributes: AttributesSchema,
|
||||||
|
metadata: Type.Object({}, { additionalProperties: true }),
|
||||||
|
createdAt: Type.Date(),
|
||||||
|
updatedAt: Type.Date(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type SelectEdge = Static<typeof SelectEdge>;
|
||||||
|
|
||||||
|
export const InsertEdge = createInsertSchema(edges, {
|
||||||
|
key: Type.String({ minLength: 1 }),
|
||||||
|
attributes: AttributesSchema,
|
||||||
|
});
|
||||||
|
|
||||||
|
export type InsertEdge = Static<typeof InsertEdge>;
|
||||||
38
src/sqlite/tables/graphTypes.ts
Normal file
38
src/sqlite/tables/graphTypes.ts
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
import { sqliteTable, text, integer } from "drizzle-orm/sqlite-core";
|
||||||
|
import { createInsertSchema, createSelectSchema } from "@alkdev/drizzlebox";
|
||||||
|
import { Type, type Static } from "@alkdev/typebox";
|
||||||
|
import { commonCols } from "./common.ts";
|
||||||
|
|
||||||
|
|
||||||
|
const ConfigSchema = Type.Object({
|
||||||
|
type: Type.Union([
|
||||||
|
Type.Literal("directed"),
|
||||||
|
Type.Literal("undirected"),
|
||||||
|
Type.Literal("mixed"),
|
||||||
|
], { default: "mixed" }),
|
||||||
|
multi: Type.Boolean({ default: true }),
|
||||||
|
allowSelfLoops: Type.Boolean({ default: true }),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const graphTypes = sqliteTable("graph_types", {
|
||||||
|
...commonCols,
|
||||||
|
name: text("name").notNull().unique(),
|
||||||
|
description: text("description").default(""),
|
||||||
|
config: text("config", { mode: "json" }).$type<Static<typeof ConfigSchema>>().notNull(),
|
||||||
|
version: integer("version").notNull().default(1),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const SelectGraphType = createSelectSchema(graphTypes, {
|
||||||
|
metadata: Type.Object({}, { additionalProperties: true }),
|
||||||
|
createdAt: Type.Date(),
|
||||||
|
updatedAt: Type.Date(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type SelectGraphType = Static<typeof SelectGraphType>;
|
||||||
|
|
||||||
|
export const InsertGraphType = createInsertSchema(graphTypes, {
|
||||||
|
name: Type.String({ minLength: 2, maxLength: 255 }),
|
||||||
|
description: Type.Optional(Type.String()),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type InsertGraphType = Static<typeof InsertGraphType>;
|
||||||
35
src/sqlite/tables/graphs.ts
Normal file
35
src/sqlite/tables/graphs.ts
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import { sqliteTable, text } from "drizzle-orm/sqlite-core";
|
||||||
|
import { createInsertSchema, createSelectSchema } from "@alkdev/drizzlebox";
|
||||||
|
import { Type, type Static } from "@alkdev/typebox";
|
||||||
|
import { commonCols } from "./common.ts";
|
||||||
|
import { graphTypes } from "./graphTypes.ts";
|
||||||
|
import { EnumGraphStatus } from "../../graphs/types.ts";
|
||||||
|
|
||||||
|
export const graphs = sqliteTable("graphs", {
|
||||||
|
...commonCols,
|
||||||
|
graphTypeId: text("graph_type_id").references(() => graphTypes.id, { onDelete: "set null" }),
|
||||||
|
name: text("name").notNull(),
|
||||||
|
description: text("description").default(""),
|
||||||
|
status: text("status", { enum: ["active", "archived", "draft"] })
|
||||||
|
.default("draft")
|
||||||
|
.notNull(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const SelectGraph = createSelectSchema(graphs, {
|
||||||
|
metadata: Type.Object({}, { additionalProperties: true }),
|
||||||
|
createdAt: Type.Date(),
|
||||||
|
updatedAt: Type.Date(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type SelectGraph = Static<typeof SelectGraph>;
|
||||||
|
|
||||||
|
export const InsertGraph = createInsertSchema(graphs, {
|
||||||
|
name: Type.String({ minLength: 2 }),
|
||||||
|
status: Type.Optional(Type.Union([
|
||||||
|
Type.Literal(EnumGraphStatus.Active),
|
||||||
|
Type.Literal(EnumGraphStatus.Archived),
|
||||||
|
Type.Literal(EnumGraphStatus.Draft),
|
||||||
|
])),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type InsertGraph = Static<typeof InsertGraph>;
|
||||||
23
src/sqlite/tables/index.ts
Normal file
23
src/sqlite/tables/index.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
export { graphs } from "./graphs.ts";
|
||||||
|
export type { SelectGraph, InsertGraph } from "./graphs.ts";
|
||||||
|
export { SelectGraph as SelectGraphSchema, InsertGraph as InsertGraphSchema } from "./graphs.ts";
|
||||||
|
export { nodes } from "./nodes.ts";
|
||||||
|
export type { InsertNode } from "./nodes.ts";
|
||||||
|
export { InsertNodeSchema } from "./nodes.ts";
|
||||||
|
export { edges } from "./edges.ts";
|
||||||
|
export type { SelectEdge, InsertEdge } from "./edges.ts";
|
||||||
|
export { SelectEdge as SelectEdgeSchema, InsertEdge as InsertEdgeSchema } from "./edges.ts";
|
||||||
|
export { graphTypes } from "./graphTypes.ts";
|
||||||
|
export type { SelectGraphType, InsertGraphType } from "./graphTypes.ts";
|
||||||
|
export { SelectGraphType as SelectGraphTypeSchema, InsertGraphType as InsertGraphTypeSchema } from "./graphTypes.ts";
|
||||||
|
export { nodeTypes } from "./nodeTypes.ts";
|
||||||
|
export type { SelectNodeType, InsertNodeType } from "./nodeTypes.ts";
|
||||||
|
export { SelectNodeType as SelectNodeTypeSchema, InsertNodeType as InsertNodeTypeSchema } from "./nodeTypes.ts";
|
||||||
|
export { edgeTypes } from "./edgeTypes.ts";
|
||||||
|
export type { SelectEdgeType, InsertEdgeType } from "./edgeTypes.ts";
|
||||||
|
export { SelectEdgeType as SelectEdgeTypeSchema, InsertEdgeType as InsertEdgeTypeSchema } from "./edgeTypes.ts";
|
||||||
|
export { actors } from "./actors.ts";
|
||||||
|
export type { SelectActor, InsertActor } from "./actors.ts";
|
||||||
|
export { SelectActor as SelectActorSchema, InsertActor as InsertActorSchema } from "./actors.ts";
|
||||||
|
export { commonCols, ACTOR_TYPE } from "./common.ts";
|
||||||
|
export type { EnumValues } from "./common.ts";
|
||||||
31
src/sqlite/tables/nodeTypes.ts
Normal file
31
src/sqlite/tables/nodeTypes.ts
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import { sqliteTable, text, unique } from "drizzle-orm/sqlite-core";
|
||||||
|
import { createInsertSchema, createSelectSchema } from "@alkdev/drizzlebox";
|
||||||
|
import { Type, type Static } from "@alkdev/typebox";
|
||||||
|
import { commonCols } from "./common.ts";
|
||||||
|
import { graphTypes } from "./graphTypes.ts";
|
||||||
|
|
||||||
|
export const nodeTypes = sqliteTable("node_types", {
|
||||||
|
...commonCols,
|
||||||
|
graphTypeId: text("graph_type_id").notNull().references(() => graphTypes.id, { onDelete: "cascade" }),
|
||||||
|
name: text("name").notNull(),
|
||||||
|
description: text("description").default(""),
|
||||||
|
schema: text("schema", { mode: "json" }).$type<Record<string, unknown>>().notNull(),
|
||||||
|
}, (table) => ({
|
||||||
|
graphTypeNameIdx: unique().on(table.graphTypeId, table.name),
|
||||||
|
}));
|
||||||
|
|
||||||
|
export const SelectNodeType = createSelectSchema(nodeTypes, {
|
||||||
|
schema: Type.Unknown(),
|
||||||
|
metadata: Type.Object({}, { additionalProperties: true }),
|
||||||
|
createdAt: Type.Date(),
|
||||||
|
updatedAt: Type.Date(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type SelectNodeType = Static<typeof SelectNodeType>;
|
||||||
|
|
||||||
|
export const InsertNodeType = createInsertSchema(nodeTypes, {
|
||||||
|
name: Type.String({ minLength: 2 }),
|
||||||
|
schema: Type.Unknown(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type InsertNodeType = Static<typeof InsertNodeType>;
|
||||||
23
src/sqlite/tables/nodes.ts
Normal file
23
src/sqlite/tables/nodes.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import { sqliteTable, text, unique } from "drizzle-orm/sqlite-core";
|
||||||
|
import { createInsertSchema } from "@alkdev/drizzlebox";
|
||||||
|
import { Type, type Static } from "@alkdev/typebox";
|
||||||
|
import { commonCols } from "./common.ts";
|
||||||
|
import { graphs } from "./graphs.ts";
|
||||||
|
|
||||||
|
const AttributesSchema = Type.Record(Type.String(), Type.Any());
|
||||||
|
|
||||||
|
export const nodes = sqliteTable("nodes", {
|
||||||
|
...commonCols,
|
||||||
|
graphId: text("graph_id").notNull().references(() => graphs.id, { onDelete: "cascade" }),
|
||||||
|
key: text("key").notNull(),
|
||||||
|
attributes: text("attributes", { mode: "json" }).notNull().default({}),
|
||||||
|
}, (table) => ({
|
||||||
|
graphKeyIdx: unique().on(table.graphId, table.key),
|
||||||
|
}));
|
||||||
|
|
||||||
|
export const InsertNodeSchema = createInsertSchema(nodes, {
|
||||||
|
key: Type.String({ minLength: 1 }),
|
||||||
|
attributes: AttributesSchema,
|
||||||
|
});
|
||||||
|
|
||||||
|
export type InsertNode = Static<typeof InsertNodeSchema>;
|
||||||
Reference in New Issue
Block a user