Add reference graph type Modules (CallGraph, SecretGraph)

This commit is contained in:
2026-05-29 12:26:43 +00:00
parent 14c7fa4746
commit 33e66bc414
5 changed files with 301 additions and 0 deletions

View File

@@ -1,3 +1,6 @@
export {
CallGraph,
} from "./modules/call-graph.ts";
export {
BaseEdgeAttributes,
BaseNodeAttributes,
@@ -6,6 +9,9 @@ export {
GraphStatus,
Metagraph,
} from "./modules/metagraph.ts";
export {
SecretGraph,
} from "./modules/secret-graph.ts";
export {
decrypt,
encrypt,

View File

@@ -0,0 +1,75 @@
import { Type } from "@alkdev/typebox";
import { Metagraph } from "./metagraph.ts";
export const CallGraph = Type.Module({
Config: Type.Object({
type: Type.Literal("directed"),
multi: Type.Literal(false),
allowSelfLoops: Type.Literal(false),
}),
CallNode: Type.Composite([
Metagraph.Import("BaseNode"),
Type.Object({
requestId: Type.String(),
operationId: Type.String(),
status: Type.Ref("CallStatus"),
parentRequestId: Type.Optional(Type.String()),
input: Type.Unknown(),
output: Type.Optional(Type.Unknown()),
identity: Type.Optional(Type.Ref("Identity")),
startedAt: Type.Optional(Type.String({ format: "date-time" })),
completedAt: Type.Optional(Type.String({ format: "date-time" })),
}),
]),
SubcallNode: Type.Composite([
Metagraph.Import("BaseNode"),
Type.Object({
requestId: Type.String(),
parentRequestId: Type.String(),
operationId: Type.String(),
status: Type.Ref("CallStatus"),
}),
]),
TriggeredEdge: Type.Composite([
Metagraph.Import("BaseEdge"),
Type.Object({
type: Type.Literal("triggered"),
}),
]),
DependsOnEdge: Type.Composite([
Metagraph.Import("BaseEdge"),
Type.Object({
type: Type.Literal("depends_on"),
}),
]),
TriggeredEdgeConstraints: Type.Object({
edgeType: Type.Literal("triggered"),
allowedSourceTypes: Type.Array(Type.String(), { default: ["Call"] }),
allowedTargetTypes: Type.Array(Type.String(), { default: ["Call", "Subcall"] }),
}),
DependsOnEdgeConstraints: Type.Object({
edgeType: Type.Literal("depends_on"),
allowedSourceTypes: Type.Array(Type.String(), { default: ["Call", "Subcall"] }),
allowedTargetTypes: Type.Array(Type.String(), { default: ["Call", "Subcall"] }),
}),
CallStatus: Type.Union([
Type.Literal("pending"),
Type.Literal("running"),
Type.Literal("completed"),
Type.Literal("failed"),
Type.Literal("aborted"),
]),
Identity: Type.Object({
id: Type.String(),
scopes: Type.Array(Type.String()),
resources: Type.Optional(Type.Record(Type.String(), Type.Array(Type.String()))),
}),
});

View File

@@ -0,0 +1,3 @@
export { CallGraph } from "./call-graph.ts";
export { Metagraph } from "./metagraph.ts";
export { SecretGraph } from "./secret-graph.ts";

View File

@@ -0,0 +1,44 @@
import { Type } from "@alkdev/typebox";
import { EncryptedDataSchema } from "../crypto.ts";
import { Metagraph } from "./metagraph.ts";
export const SecretGraph = Type.Module({
Config: Type.Object({
type: Type.Literal("undirected"),
multi: Type.Literal(false),
allowSelfLoops: Type.Literal(false),
}),
SecretNode: Type.Composite([
Metagraph.Import("BaseNode"),
Type.Object({
key: Type.String({ minLength: 1, maxLength: 255 }),
encryptedData: EncryptedDataSchema,
expiresAt: Type.Optional(Type.String({ format: "date-time" })),
}),
]),
ClientNode: Type.Composite([
Metagraph.Import("BaseNode"),
Type.Object({
name: Type.String(),
type: Type.String(),
config: Type.Record(Type.String(), Type.Unknown()),
enabled: Type.Boolean({ default: true }),
}),
]),
HasSecretEdge: Type.Composite([
Metagraph.Import("BaseEdge"),
Type.Object({
type: Type.Literal("has_secret"),
secretKey: Type.String(),
}),
]),
HasSecretEdgeConstraints: Type.Object({
edgeType: Type.Literal("has_secret"),
allowedSourceTypes: Type.Array(Type.String(), { default: ["Client"] }),
allowedTargetTypes: Type.Array(Type.String(), { default: ["Secret"] }),
}),
});

View File

@@ -0,0 +1,173 @@
import { assertEquals } from "@std/assert";
import {
CallGraph,
moduleToDbSchema,
SecretGraph,
validateEdge,
validateNode,
} from "../mod.ts";
import type { TModule, TProperties } from "@alkdev/typebox";
Deno.test("CallGraph has required entries", () => {
const entries = Object.keys(
(CallGraph.Import("Config" as never).$defs as Record<string, unknown>),
);
const required = [
"Config",
"CallNode",
"SubcallNode",
"TriggeredEdge",
"DependsOnEdge",
"TriggeredEdgeConstraints",
"DependsOnEdgeConstraints",
"CallStatus",
"Identity",
];
for (const name of required) {
assertEquals(entries.includes(name), true, `Missing entry: ${name}`);
}
});
Deno.test("CallGraph.Config uses literal values", () => {
const configImport = CallGraph.Import("Config" as never);
const configSchema = configImport.$defs.Config as Record<string, unknown>;
const properties = configSchema.properties as Record<string, unknown>;
const typeProp = properties.type as Record<string, unknown>;
assertEquals(typeProp.const, "directed");
const multiProp = properties.multi as Record<string, unknown>;
assertEquals(multiProp.const, false);
const allowSelfLoopsProp = properties.allowSelfLoops as Record<string, unknown>;
assertEquals(allowSelfLoopsProp.const, false);
});
Deno.test("validateNode with CallGraph CallNode returns true for valid data", () => {
const result = validateNode(
CallGraph as unknown as TModule<TProperties>,
"CallNode",
{
requestId: "req-001",
operationId: "op-001",
status: "pending",
input: {},
},
);
assertEquals(result, true);
});
Deno.test("validateNode with CallGraph CallNode returns false for invalid data", () => {
const result = validateNode(
CallGraph as unknown as TModule<TProperties>,
"CallNode",
{
requestId: "req-001",
},
);
assertEquals(result, false);
});
Deno.test("moduleToDbSchema with CallGraph produces valid DbSchema", () => {
const schema = moduleToDbSchema(
CallGraph as unknown as TModule<TProperties>,
"call-graph",
);
assertEquals(schema.graphType.name, "call-graph");
assertEquals(schema.graphType.config.properties !== undefined, true);
assertEquals(schema.nodeTypes.length, 2);
assertEquals(schema.nodeTypes[0].name, "Call");
assertEquals(schema.nodeTypes[1].name, "Subcall");
assertEquals(schema.edgeTypes.length, 2);
assertEquals(schema.edgeTypes[0].name, "Triggered");
assertEquals(schema.edgeTypes[1].name, "DependsOn");
});
Deno.test("SecretGraph has required entries", () => {
const entries = Object.keys(
(SecretGraph.Import("Config" as never).$defs as Record<string, unknown>),
);
const required = [
"Config",
"SecretNode",
"ClientNode",
"HasSecretEdge",
"HasSecretEdgeConstraints",
];
for (const name of required) {
assertEquals(entries.includes(name), true, `Missing entry: ${name}`);
}
});
Deno.test("SecretGraph.Config uses literal values", () => {
const configImport = SecretGraph.Import("Config" as never);
const configSchema = configImport.$defs.Config as Record<string, unknown>;
const properties = configSchema.properties as Record<string, unknown>;
const typeProp = properties.type as Record<string, unknown>;
assertEquals(typeProp.const, "undirected");
const multiProp = properties.multi as Record<string, unknown>;
assertEquals(multiProp.const, false);
const allowSelfLoopsProp = properties.allowSelfLoops as Record<string, unknown>;
assertEquals(allowSelfLoopsProp.const, false);
});
Deno.test("moduleToDbSchema with SecretGraph produces valid DbSchema", () => {
const schema = moduleToDbSchema(
SecretGraph as unknown as TModule<TProperties>,
"secret-graph",
);
assertEquals(schema.graphType.name, "secret-graph");
assertEquals(schema.nodeTypes.length, 2);
assertEquals(schema.nodeTypes[0].name, "Secret");
assertEquals(schema.nodeTypes[1].name, "Client");
assertEquals(schema.edgeTypes.length, 1);
assertEquals(schema.edgeTypes[0].name, "HasSecret");
});
Deno.test("validateNode with SecretGraph SecretNode validates encrypted data", () => {
const result = validateNode(
SecretGraph as unknown as TModule<TProperties>,
"SecretNode",
{
key: "api_key",
encryptedData: {
keyVersion: 1,
salt: "dGVzdA==",
iv: "dGVzdA==dGVzdA==",
data: "dGVzdA==",
},
},
);
assertEquals(result, true);
});
Deno.test("validateEdge with CallGraph TriggeredEdge returns true for valid data", () => {
const result = validateEdge(
CallGraph as unknown as TModule<TProperties>,
"TriggeredEdge",
{
type: "triggered",
},
);
assertEquals(result, true);
});
Deno.test("validateEdge with CallGraph TriggeredEdge returns false for wrong type", () => {
const result = validateEdge(
CallGraph as unknown as TModule<TProperties>,
"TriggeredEdge",
{
type: "depends_on",
},
);
assertEquals(result, false);
});