Skip to content

Commit 0695fb7

Browse files
committed
Merge remote-tracking branch 'origin/develop' into vm_multi-pass
2 parents de6ccee + 02573eb commit 0695fb7

55 files changed

Lines changed: 3081 additions & 1682 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

docs/agent-planning-scenarios.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ User right-clicks any node and sees:
1616

1717
```
1818
┌─────────────────────────────┐
19-
│ 📋 Edit Node
19+
│ 📋 Edit Work Item
2020
│ 🔗 Add Relationship │
2121
│ 🎯 Set Priority │
2222
│ ───────────────────────── │

docs/features/graph-creation.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,12 +51,12 @@ GraphDone uses a **graph-first approach** where work items exist as connected no
5151

5252
**Quick Node Creation:**
5353
1. **Right-click** on empty graph space
54-
2. **Select** "Add Node" from context menu
54+
2. **Select** "Add Work Item" from context menu
5555
3. **Choose** node type from comprehensive list
5656
4. **Fill** essential information and save
5757

5858
**Detailed Node Creation:**
59-
1. **Click** primary "Add Node" button
59+
1. **Click** primary "Add Work Item" button
6060
2. **Complete** the node creation form:
6161

6262
**Essential Fields:**

packages/server/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@
1414
"typecheck": "tsc --noEmit",
1515
"clean": "rm -rf dist coverage",
1616
"db:seed": "tsx src/scripts/seed.ts",
17-
"create-admin": "tsx src/scripts/create-admin.ts"
17+
"create-admin": "tsx src/scripts/create-admin.ts",
18+
"create-welcome-graphs": "tsx src/scripts/create-welcome-graphs.ts"
1819
},
1920
"dependencies": {
2021
"@apollo/server": "^4.9.0",

packages/server/src/index.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { typeDefs } from './schema/neo4j-schema.js';
1818
import { authTypeDefs } from './schema/auth-schema.js';
1919
import { authOnlyTypeDefs } from './schema/auth-only-schema.js';
2020
import { sqliteAuthResolvers } from './resolvers/sqlite-auth.js';
21+
import { graphProtectionResolvers } from './resolvers/graph-protection.js';
2122
import { extractUserFromToken } from './middleware/auth.js';
2223
import { mergeTypeDefs } from '@graphql-tools/merge';
2324
import { driver, NEO4J_URI } from './db.js';
@@ -293,16 +294,25 @@ async function startServer() {
293294
console.log('ℹ️ Default admin setup completed'); // eslint-disable-line no-console
294295
}
295296

297+
// Welcome graphs are now created per-user on first login (handled by frontend)
298+
296299
// Merge type definitions (Neo4j schema + auth schema)
297300
const mergedTypeDefs = mergeTypeDefs([typeDefs, authTypeDefs]);
298301

299-
// Create Neo4jGraphQL instance for graph data with SQLite auth resolvers override
302+
// Create Neo4jGraphQL instance with custom resolvers
300303
const neoSchema = new Neo4jGraphQL({
301304
typeDefs: mergedTypeDefs,
302305
driver,
303306
resolvers: {
304-
// Override auth resolvers to use SQLite instead of Neo4j User nodes
305-
...sqliteAuthResolvers,
307+
Mutation: {
308+
// Auth resolvers (SQLite-based)
309+
...sqliteAuthResolvers.Mutation,
310+
// Graph protection (prevent Welcome graph deletion)
311+
...graphProtectionResolvers.Mutation,
312+
},
313+
Query: {
314+
...sqliteAuthResolvers.Query,
315+
}
306316
},
307317
});
308318

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import { GraphQLError } from 'graphql';
2+
import { Driver } from 'neo4j-driver';
3+
4+
const PROTECTED_GRAPHS = ['welcome-graph-shared'];
5+
6+
async function isProtectedGraph(driver: Driver, graphId: string): Promise<boolean> {
7+
if (PROTECTED_GRAPHS.includes(graphId)) {
8+
return true;
9+
}
10+
11+
const session = driver.session();
12+
try {
13+
const result = await session.run(
14+
`MATCH (g:Graph {id: $graphId}) RETURN g.id IN $protectedIds AS isProtected`,
15+
{ graphId, protectedIds: PROTECTED_GRAPHS }
16+
);
17+
return result.records[0]?.get('isProtected') || false;
18+
} finally {
19+
await session.close();
20+
}
21+
}
22+
23+
24+
export const graphProtectionResolvers = {
25+
Mutation: {
26+
// Protect graphs from deletion
27+
deleteGraphs: async (
28+
_parent: any,
29+
args: { where?: { id?: string; id_IN?: string[] } },
30+
context: { driver: Driver | null }
31+
) => {
32+
if (!context.driver) {
33+
throw new GraphQLError('Database connection unavailable');
34+
}
35+
36+
const graphId = args.where?.id;
37+
const graphIds = args.where?.id_IN;
38+
const idsToCheck = graphId ? [graphId] : (graphIds || []);
39+
40+
for (const id of idsToCheck) {
41+
if (await isProtectedGraph(context.driver, id)) {
42+
throw new GraphQLError('The Welcome graph is read-only and cannot be deleted. This is a system tutorial graph for all users.', {
43+
extensions: {
44+
code: 'FORBIDDEN',
45+
protectedGraph: true,
46+
graphId: id
47+
}
48+
});
49+
}
50+
}
51+
52+
const session = context.driver.session();
53+
try {
54+
const result = await session.run(
55+
`
56+
MATCH (g:Graph)
57+
WHERE g.id = $id ${graphIds ? 'OR g.id IN $ids' : ''}
58+
DETACH DELETE g
59+
RETURN count(g) AS nodesDeleted
60+
`,
61+
{ id: graphId, ids: graphIds }
62+
);
63+
64+
return {
65+
nodesDeleted: result.records[0]?.get('nodesDeleted')?.toNumber() || 0
66+
};
67+
} finally {
68+
await session.close();
69+
}
70+
}
71+
72+
}
73+
};

packages/server/src/resolvers/sqlite-auth.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -368,7 +368,7 @@ export const sqliteAuthResolvers = {
368368

369369
const token = generateToken(user.id, user.email, user.role);
370370
console.log(`✅ Login successful: ${user.username} (${user.role})`);
371-
371+
372372
return {
373373
token,
374374
user: {
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { driver } from '../db.js';
2+
import { createSharedWelcomeGraph, sharedWelcomeGraphExists } from '../services/onboarding.js';
3+
4+
async function ensureSharedWelcomeGraph() {
5+
console.log('🎉 Ensuring shared Welcome graph exists...\n');
6+
7+
try {
8+
const exists = await sharedWelcomeGraphExists(driver);
9+
10+
if (exists) {
11+
console.log('⏭️ Shared Welcome graph already exists - skipping creation\n');
12+
console.log('========================================');
13+
console.log(' Welcome Graph Already Exists ');
14+
console.log('========================================\n');
15+
} else {
16+
await createSharedWelcomeGraph(driver);
17+
console.log('✅ Shared Welcome graph created successfully!\n');
18+
console.log('========================================');
19+
console.log(' Welcome Graph Created ');
20+
console.log('========================================');
21+
console.log('• Graph Name: Welcome');
22+
console.log('• Type: PROJECT');
23+
console.log('• Shared: Yes (all users can access)');
24+
console.log('• Permissions: Read-only for all users');
25+
console.log('• Contains: 6 tutorial nodes');
26+
console.log('========================================\n');
27+
}
28+
29+
} catch (error: any) {
30+
console.error('❌ Failed to create shared Welcome graph:', error);
31+
process.exit(1);
32+
} finally {
33+
await driver.close();
34+
process.exit(0);
35+
}
36+
}
37+
38+
ensureSharedWelcomeGraph();

packages/server/src/scripts/seed.ts

Lines changed: 107 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -28,64 +28,107 @@ async function seed() {
2828
await session.run('MATCH (n) DETACH DELETE n');
2929
// eslint-disable-next-line no-console
3030
console.log('✨ Cleared existing data');
31-
32-
// Create work items with proper team IDs
31+
32+
// Create graphs first
33+
const graphs = [
34+
{
35+
id: 'welcome-graph-shared',
36+
name: 'Welcome to GraphDone',
37+
description: 'A tutorial graph to help you understand GraphDone',
38+
isPublic: true,
39+
teamId: 'team-1',
40+
userId: 'user-1'
41+
},
42+
{
43+
id: 'graph-project-alpha',
44+
name: 'Project Alpha',
45+
description: 'Main development project',
46+
isPublic: false,
47+
teamId: 'team-1',
48+
userId: 'user-1'
49+
},
50+
{
51+
id: 'graph-test-beta',
52+
name: 'Test Graph Beta',
53+
description: 'Testing and experimentation',
54+
isPublic: false,
55+
teamId: 'team-1',
56+
userId: 'user-2'
57+
}
58+
];
59+
60+
for (const graph of graphs) {
61+
await session.run(
62+
`CREATE (g:Graph {
63+
id: $id,
64+
name: $name,
65+
description: $description,
66+
isPublic: $isPublic,
67+
teamId: $teamId,
68+
userId: $userId,
69+
createdAt: datetime(),
70+
updatedAt: datetime()
71+
})`,
72+
graph
73+
);
74+
}
75+
// eslint-disable-next-line no-console
76+
console.log(`✅ Created ${graphs.length} graphs`);
77+
78+
// Create work items with proper team IDs and graph assignments
3379
const workItems = [
34-
// Infrastructure & Setup
35-
{ id: 'wi-1', title: 'Set up Neo4j database', type: 'TASK', status: 'COMPLETED', teamId: 'team-1', userId: 'user-1' },
36-
{ id: 'wi-2', title: 'Configure GraphQL schema', type: 'TASK', status: 'COMPLETED', teamId: 'team-1', userId: 'user-1' },
37-
{ id: 'wi-3', title: 'Implement authentication', type: 'TASK', status: 'IN_PROGRESS', teamId: 'team-1', userId: 'user-2' },
38-
{ id: 'wi-4', title: 'Set up CI/CD pipeline', type: 'TASK', status: 'PLANNED', teamId: 'team-1', userId: 'user-3' },
39-
40-
// Core Features
41-
{ id: 'wi-5', title: 'Graph visualization system', type: 'MILESTONE', status: 'IN_PROGRESS', teamId: 'team-1', userId: 'user-1' },
42-
{ id: 'wi-6', title: 'Implement D3.js force layout', type: 'TASK', status: 'COMPLETED', teamId: 'team-1', userId: 'user-2' },
43-
{ id: 'wi-7', title: 'Add node drag interaction', type: 'TASK', status: 'COMPLETED', teamId: 'team-1', userId: 'user-2' },
44-
{ id: 'wi-8', title: 'Create edge rendering system', type: 'TASK', status: 'IN_PROGRESS', teamId: 'team-1', userId: 'user-3' },
45-
46-
// Ideas & Proposals
47-
{ id: 'wi-9', title: 'AI agent integration', type: 'IDEA', status: 'PROPOSED', teamId: 'team-1', userId: 'user-4' },
48-
{ id: 'wi-10', title: 'Mobile app development', type: 'IDEA', status: 'PROPOSED', teamId: 'team-1', userId: 'user-5' },
49-
{ id: 'wi-11', title: 'Real-time collaboration', type: 'IDEA', status: 'PROPOSED', teamId: 'team-1', userId: 'user-1' },
50-
51-
// Outcomes
52-
{ id: 'wi-12', title: 'Production-ready graph system', type: 'OUTCOME', status: 'PLANNED', teamId: 'team-1', userId: 'user-1' },
53-
{ id: 'wi-13', title: 'Scalable architecture', type: 'OUTCOME', status: 'IN_PROGRESS', teamId: 'team-1', userId: 'user-2' },
54-
55-
// Additional tasks for testing
56-
{ id: 'wi-14', title: 'Write unit tests', type: 'TASK', status: 'IN_PROGRESS', teamId: 'team-1', userId: 'user-3' },
57-
{ id: 'wi-15', title: 'Performance optimization', type: 'TASK', status: 'PLANNED', teamId: 'team-1', userId: 'user-4' },
58-
{ id: 'wi-16', title: 'Documentation update', type: 'TASK', status: 'PLANNED', teamId: 'team-1', userId: 'user-5' },
59-
60-
// More features
61-
{ id: 'wi-17', title: 'User dashboard', type: 'MILESTONE', status: 'PLANNED', teamId: 'team-1', userId: 'user-1' },
62-
{ id: 'wi-18', title: 'Analytics module', type: 'MILESTONE', status: 'PROPOSED', teamId: 'team-1', userId: 'user-2' },
63-
{ id: 'wi-19', title: 'Export functionality', type: 'TASK', status: 'PROPOSED', teamId: 'team-1', userId: 'user-3' },
64-
{ id: 'wi-20', title: 'Import from other tools', type: 'TASK', status: 'PROPOSED', teamId: 'team-1', userId: 'user-4' },
65-
66-
// Test data variations
67-
{ id: 'wi-21', title: 'Test WithAPOC', type: 'TASK', status: 'COMPLETED', teamId: 'team-1', userId: 'user-1' },
68-
{ id: 'wi-22', title: 'testUI Test', type: 'TASK', status: 'IN_PROGRESS', teamId: 'team-1', userId: 'user-2' },
69-
{ id: 'wi-23', title: 'Form validation testing', type: 'TASK', status: 'PLANNED', teamId: 'team-1', userId: 'user-3' },
70-
{ id: 'wi-24', title: 'Edge case handling', type: 'TASK', status: 'PROPOSED', teamId: 'team-1', userId: 'user-4' },
80+
// Welcome Graph Items (tutorial)
81+
{ id: 'wi-welcome-1', title: 'Welcome to GraphDone!', type: 'MILESTONE', status: 'COMPLETED', teamId: 'team-1', userId: 'user-1', graphId: 'welcome-graph-shared' },
82+
{ id: 'wi-welcome-2', title: 'Create your first work item', type: 'TASK', status: 'COMPLETED', teamId: 'team-1', userId: 'user-1', graphId: 'welcome-graph-shared' },
83+
{ id: 'wi-welcome-3', title: 'Connect work items together', type: 'TASK', status: 'IN_PROGRESS', teamId: 'team-1', userId: 'user-1', graphId: 'welcome-graph-shared' },
84+
{ id: 'wi-welcome-4', title: 'Explore different views', type: 'TASK', status: 'PLANNED', teamId: 'team-1', userId: 'user-1', graphId: 'welcome-graph-shared' },
85+
86+
// Project Alpha - Infrastructure & Setup
87+
{ id: 'wi-1', title: 'Set up Neo4j database', type: 'TASK', status: 'COMPLETED', teamId: 'team-1', userId: 'user-1', graphId: 'graph-project-alpha' },
88+
{ id: 'wi-2', title: 'Configure GraphQL schema', type: 'TASK', status: 'COMPLETED', teamId: 'team-1', userId: 'user-1', graphId: 'graph-project-alpha' },
89+
{ id: 'wi-3', title: 'Implement authentication', type: 'TASK', status: 'IN_PROGRESS', teamId: 'team-1', userId: 'user-2', graphId: 'graph-project-alpha' },
90+
{ id: 'wi-4', title: 'Set up CI/CD pipeline', type: 'TASK', status: 'PLANNED', teamId: 'team-1', userId: 'user-3', graphId: 'graph-project-alpha' },
7191

72-
// Strategic items
73-
{ id: 'wi-25', title: 'Q1 2024 Planning', type: 'MILESTONE', status: 'COMPLETED', teamId: 'team-1', userId: 'user-1' },
74-
{ id: 'wi-26', title: 'Product roadmap review', type: 'OUTCOME', status: 'IN_PROGRESS', teamId: 'team-1', userId: 'user-2' },
75-
{ id: 'wi-27', title: 'Customer feedback analysis', type: 'TASK', status: 'PLANNED', teamId: 'team-1', userId: 'user-3' },
76-
{ id: 'wi-28', title: 'Market research', type: 'TASK', status: 'PROPOSED', teamId: 'team-1', userId: 'user-4' },
92+
// Project Alpha - Core Features
93+
{ id: 'wi-5', title: 'Graph visualization system', type: 'MILESTONE', status: 'IN_PROGRESS', teamId: 'team-1', userId: 'user-1', graphId: 'graph-project-alpha' },
94+
{ id: 'wi-6', title: 'Implement D3.js force layout', type: 'TASK', status: 'COMPLETED', teamId: 'team-1', userId: 'user-2', graphId: 'graph-project-alpha' },
95+
{ id: 'wi-7', title: 'Add node drag interaction', type: 'TASK', status: 'COMPLETED', teamId: 'team-1', userId: 'user-2', graphId: 'graph-project-alpha' },
96+
{ id: 'wi-8', title: 'Create edge rendering system', type: 'TASK', status: 'IN_PROGRESS', teamId: 'team-1', userId: 'user-3', graphId: 'graph-project-alpha' },
7797

78-
// Technical debt
79-
{ id: 'wi-29', title: 'Refactor graph engine', type: 'TASK', status: 'PLANNED', teamId: 'team-1', userId: 'user-5' },
80-
{ id: 'wi-30', title: 'Update dependencies', type: 'TASK', status: 'IN_PROGRESS', teamId: 'team-1', userId: 'user-1' },
81-
{ id: 'wi-31', title: 'Security audit', type: 'MILESTONE', status: 'PLANNED', teamId: 'team-1', userId: 'user-2' },
82-
{ id: 'wi-32', title: 'Performance benchmarking', type: 'TASK', status: 'PROPOSED', teamId: 'team-1', userId: 'user-3' }
98+
// Project Alpha - Ideas & More
99+
{ id: 'wi-9', title: 'AI agent integration', type: 'IDEA', status: 'PROPOSED', teamId: 'team-1', userId: 'user-4', graphId: 'graph-project-alpha' },
100+
{ id: 'wi-10', title: 'Mobile app development', type: 'IDEA', status: 'PROPOSED', teamId: 'team-1', userId: 'user-5', graphId: 'graph-project-alpha' },
101+
{ id: 'wi-11', title: 'Real-time collaboration', type: 'IDEA', status: 'PROPOSED', teamId: 'team-1', userId: 'user-1', graphId: 'graph-project-alpha' },
102+
{ id: 'wi-12', title: 'Production-ready graph system', type: 'OUTCOME', status: 'PLANNED', teamId: 'team-1', userId: 'user-1', graphId: 'graph-project-alpha' },
103+
{ id: 'wi-13', title: 'Scalable architecture', type: 'OUTCOME', status: 'IN_PROGRESS', teamId: 'team-1', userId: 'user-2', graphId: 'graph-project-alpha' },
104+
105+
// Test Graph Beta - Testing items
106+
{ id: 'wi-14', title: 'Write unit tests', type: 'TASK', status: 'IN_PROGRESS', teamId: 'team-1', userId: 'user-3', graphId: 'graph-test-beta' },
107+
{ id: 'wi-15', title: 'Performance optimization', type: 'TASK', status: 'PLANNED', teamId: 'team-1', userId: 'user-4', graphId: 'graph-test-beta' },
108+
{ id: 'wi-16', title: 'Documentation update', type: 'TASK', status: 'PLANNED', teamId: 'team-1', userId: 'user-5', graphId: 'graph-test-beta' },
109+
{ id: 'wi-17', title: 'User dashboard', type: 'MILESTONE', status: 'PLANNED', teamId: 'team-1', userId: 'user-1', graphId: 'graph-test-beta' },
110+
{ id: 'wi-18', title: 'Analytics module', type: 'MILESTONE', status: 'PROPOSED', teamId: 'team-1', userId: 'user-2', graphId: 'graph-test-beta' },
111+
{ id: 'wi-19', title: 'Export functionality', type: 'TASK', status: 'PROPOSED', teamId: 'team-1', userId: 'user-3', graphId: 'graph-test-beta' },
112+
{ id: 'wi-20', title: 'Import from other tools', type: 'TASK', status: 'PROPOSED', teamId: 'team-1', userId: 'user-4', graphId: 'graph-test-beta' },
113+
{ id: 'wi-21', title: 'Test WithAPOC', type: 'TASK', status: 'COMPLETED', teamId: 'team-1', userId: 'user-1', graphId: 'graph-test-beta' },
114+
{ id: 'wi-22', title: 'testUI Test', type: 'TASK', status: 'IN_PROGRESS', teamId: 'team-1', userId: 'user-2', graphId: 'graph-test-beta' },
115+
{ id: 'wi-23', title: 'Form validation testing', type: 'TASK', status: 'PLANNED', teamId: 'team-1', userId: 'user-3', graphId: 'graph-test-beta' },
116+
{ id: 'wi-24', title: 'Edge case handling', type: 'TASK', status: 'PROPOSED', teamId: 'team-1', userId: 'user-4', graphId: 'graph-test-beta' },
117+
{ id: 'wi-25', title: 'Q1 2024 Planning', type: 'MILESTONE', status: 'COMPLETED', teamId: 'team-1', userId: 'user-1', graphId: 'graph-test-beta' },
118+
{ id: 'wi-26', title: 'Product roadmap review', type: 'OUTCOME', status: 'IN_PROGRESS', teamId: 'team-1', userId: 'user-2', graphId: 'graph-test-beta' },
119+
{ id: 'wi-27', title: 'Customer feedback analysis', type: 'TASK', status: 'PLANNED', teamId: 'team-1', userId: 'user-3', graphId: 'graph-test-beta' },
120+
{ id: 'wi-28', title: 'Market research', type: 'TASK', status: 'PROPOSED', teamId: 'team-1', userId: 'user-4', graphId: 'graph-test-beta' },
121+
{ id: 'wi-29', title: 'Refactor graph engine', type: 'TASK', status: 'PLANNED', teamId: 'team-1', userId: 'user-5', graphId: 'graph-test-beta' },
122+
{ id: 'wi-30', title: 'Update dependencies', type: 'TASK', status: 'IN_PROGRESS', teamId: 'team-1', userId: 'user-1', graphId: 'graph-test-beta' },
123+
{ id: 'wi-31', title: 'Security audit', type: 'MILESTONE', status: 'PLANNED', teamId: 'team-1', userId: 'user-2', graphId: 'graph-test-beta' },
124+
{ id: 'wi-32', title: 'Performance benchmarking', type: 'TASK', status: 'PROPOSED', teamId: 'team-1', userId: 'user-3', graphId: 'graph-test-beta' }
83125
];
84126

85-
// Create work items
127+
// Create work items and link to graphs
86128
for (const item of workItems) {
87129
await session.run(
88-
`CREATE (w:WorkItem {
130+
`MATCH (g:Graph {id: $graphId})
131+
CREATE (w:WorkItem {
89132
id: $id,
90133
title: $title,
91134
type: $type,
@@ -106,7 +149,8 @@ async function seed() {
106149
tags: $tags,
107150
createdAt: datetime(),
108151
updatedAt: datetime()
109-
})`,
152+
})
153+
CREATE (w)-[:BELONGS_TO]->(g)`,
110154
{
111155
...item,
112156
description: `Description for ${item.title}`,
@@ -125,12 +169,22 @@ async function seed() {
125169

126170
// Create edges (relationships between work items)
127171
const edges = [
172+
// Welcome graph edges
173+
{ source: 'wi-welcome-1', target: 'wi-welcome-2', type: 'DEPENDS_ON' },
174+
{ source: 'wi-welcome-2', target: 'wi-welcome-3', type: 'DEPENDS_ON' },
175+
{ source: 'wi-welcome-3', target: 'wi-welcome-4', type: 'DEPENDS_ON' },
176+
177+
// Project Alpha edges
128178
{ source: 'wi-1', target: 'wi-2', type: 'DEPENDS_ON' },
129179
{ source: 'wi-2', target: 'wi-3', type: 'DEPENDS_ON' },
130180
{ source: 'wi-5', target: 'wi-6', type: 'IS_PART_OF' },
131181
{ source: 'wi-5', target: 'wi-7', type: 'IS_PART_OF' },
132182
{ source: 'wi-5', target: 'wi-8', type: 'IS_PART_OF' },
133-
{ source: 'wi-12', target: 'wi-5', type: 'DEPENDS_ON' }
183+
{ source: 'wi-12', target: 'wi-5', type: 'DEPENDS_ON' },
184+
185+
// Test graph edges
186+
{ source: 'wi-14', target: 'wi-15', type: 'DEPENDS_ON' },
187+
{ source: 'wi-17', target: 'wi-18', type: 'IS_PART_OF' }
134188
];
135189

136190
for (const edge of edges) {

0 commit comments

Comments
 (0)