fix: resolve CI failures — add StatusType to web constants, remove unused imports

Adds StatusType entry to both NODE_COLORS and NODE_SIZES Record objects
in gitnexus-web to satisfy the NodeLabel type constraint. Removes unused
DetectedStatusType and DetectedTransition type imports from test file.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Test 2026-04-01 12:46:27 +02:00
parent 83f79729b2
commit 7e598b4546
5 changed files with 93 additions and 109 deletions

View file

@ -38,6 +38,7 @@ export const NODE_COLORS: Record<NodeLabel, string> = {
Template: '#a78bfa', // Violet light - like Type
Route: '#f43f5e', // Rose - like Process
Tool: '#a855f7', // Purple - like Project
StatusType: '#06b6d4', // Cyan - workflow state
};
// Node sizes by type - clear visual hierarchy with dramatic size differences
@ -79,6 +80,7 @@ export const NODE_SIZES: Record<NodeLabel, number> = {
Template: 3, // Like Type
Route: 5, // Like Enum
Tool: 5, // Like Enum
StatusType: 5, // Like Enum - workflow state
};
// Community color palette for cluster-based coloring

View file

@ -2975,6 +2975,7 @@
"resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz",
"integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==",
"license": "MIT",
"peer": true,
"dependencies": {
"accepts": "~1.3.8",
"array-flatten": "1.1.1",
@ -3423,6 +3424,7 @@
"resolved": "https://registry.npmjs.org/hono/-/hono-4.12.9.tgz",
"integrity": "sha512-wy3T8Zm2bsEvxKZM5w21VdHDDcwVS1yUFFY6i8UobSsKfFceT7TOwhbhfKsDyx7tYQlmRM5FLpIuYvNFyjctiA==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=16.9.0"
}
@ -4324,6 +4326,7 @@
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
"dev": true,
"license": "MIT",
"peer": true,
"engines": {
"node": ">=12"
},
@ -5012,6 +5015,7 @@
"integrity": "sha512-7dxoA6kYvtgWw80265MyqJlkRl4yawIjO7S5MigytjELkX43fV2WsAXzsNfO7sBpPPCF5Gp0+XzHk0DwLCq3xQ==",
"hasInstallScript": true,
"license": "MIT",
"peer": true,
"dependencies": {
"node-addon-api": "^8.0.0",
"node-gyp-build": "^4.8.0"
@ -5358,6 +5362,7 @@
"integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"esbuild": "~0.27.0",
"get-tsconfig": "^4.7.5"
@ -5478,6 +5483,7 @@
"integrity": "sha512-dbU7/iLVa8KZALJyLOBOQ88nOXtNG8vxKuOT4I2mD+Ya70KPceF4IAmDsmU0h1Qsn5bPrvsY9HJstCRh3hG6Uw==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"lightningcss": "^1.32.0",
"picomatch": "^4.0.4",
@ -5556,6 +5562,7 @@
"integrity": "sha512-tFuJqTxKb8AvfyqMfnavXdzfy3h3sWZRWwfluGbkeR7n0HUev+FmNgZ8SDrRBTVrVCjgH5cA21qGbCffMNtWvg==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@vitest/expect": "4.1.4",
"@vitest/mocker": "4.1.4",
@ -5745,6 +5752,7 @@
"resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz",
"integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==",
"license": "MIT",
"peer": true,
"funding": {
"url": "https://github.com/sponsors/colinhacks"
}

View file

@ -1,41 +1,39 @@
import { describe, it, expect } from "vitest";
import { describe, it, expect } from 'vitest';
import {
extractStatusTypes,
extractStatusTransitions,
type DetectedStatusType,
type DetectedTransition,
} from "../../src/core/ingestion/workflow-detector.js";
} from '../../src/core/ingestion/workflow-detector.js';
describe("extractStatusTypes", () => {
it("extracts TypeScript union type status definitions", () => {
describe('extractStatusTypes', () => {
it('extracts TypeScript union type status definitions', () => {
const content = `
export type GrantStatus = 'DRAFT' | 'ACTIVE' | 'AT_RISK' | 'ON_HOLD' | 'COMPLETED' | 'CLOSED';
export type ProjectStatus = 'NOT_STARTED' | 'IN_PROGRESS' | 'COMPLETED' | 'CANCELLED';
`;
const types = extractStatusTypes(content, "typescript", "status-rules.ts");
const types = extractStatusTypes(content, 'typescript', 'status-rules.ts');
expect(types).toHaveLength(2);
expect(types[0]).toMatchObject({
name: "GrantStatus",
values: ["DRAFT", "ACTIVE", "AT_RISK", "ON_HOLD", "COMPLETED", "CLOSED"],
filePath: "status-rules.ts",
name: 'GrantStatus',
values: ['DRAFT', 'ACTIVE', 'AT_RISK', 'ON_HOLD', 'COMPLETED', 'CLOSED'],
filePath: 'status-rules.ts',
});
expect(types[1]).toMatchObject({
name: "ProjectStatus",
values: ["NOT_STARTED", "IN_PROGRESS", "COMPLETED", "CANCELLED"],
name: 'ProjectStatus',
values: ['NOT_STARTED', 'IN_PROGRESS', 'COMPLETED', 'CANCELLED'],
});
});
it("extracts lowercase approval status types", () => {
it('extracts lowercase approval status types', () => {
const content = `
export type ApprovalInstanceStatus = 'pending' | 'approved' | 'rejected' | 'escalated' | 'cancelled';
`;
const types = extractStatusTypes(content, "typescript", "workflow.ts");
const types = extractStatusTypes(content, 'typescript', 'workflow.ts');
expect(types).toHaveLength(1);
expect(types[0].values).toContain("pending");
expect(types[0].values).toContain("approved");
expect(types[0].values).toContain('pending');
expect(types[0].values).toContain('approved');
});
it("extracts enum-based status definitions", () => {
it('extracts enum-based status definitions', () => {
const content = `
enum OrderStatus {
PENDING = 'pending',
@ -44,61 +42,56 @@ enum OrderStatus {
DELIVERED = 'delivered',
}
`;
const types = extractStatusTypes(content, "typescript", "order.ts");
const types = extractStatusTypes(content, 'typescript', 'order.ts');
expect(types).toHaveLength(1);
expect(types[0].name).toBe("OrderStatus");
expect(types[0].values).toEqual([
"pending",
"processing",
"shipped",
"delivered",
]);
expect(types[0].name).toBe('OrderStatus');
expect(types[0].values).toEqual(['pending', 'processing', 'shipped', 'delivered']);
});
it("ignores non-status union types", () => {
it('ignores non-status union types', () => {
const content = `
export type Color = 'red' | 'blue' | 'green';
export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE';
`;
const types = extractStatusTypes(content, "typescript", "utils.ts");
const types = extractStatusTypes(content, 'typescript', 'utils.ts');
// HttpMethod might match due to keyword heuristics, but Color should not
expect(types.find((t) => t.name === "Color")).toBeUndefined();
expect(types.find((t) => t.name === 'Color')).toBeUndefined();
});
it("extracts Python Enum status types", () => {
it('extracts Python Enum status types', () => {
const content = `
class OrderStatus(Enum):
PENDING = 'pending'
SHIPPED = 'shipped'
DELIVERED = 'delivered'
`;
const types = extractStatusTypes(content, "python", "models.py");
const types = extractStatusTypes(content, 'python', 'models.py');
expect(types).toHaveLength(1);
expect(types[0]).toMatchObject({
name: "OrderStatus",
values: ["pending", "shipped", "delivered"],
kind: "enum",
filePath: "models.py",
name: 'OrderStatus',
values: ['pending', 'shipped', 'delivered'],
kind: 'enum',
filePath: 'models.py',
});
});
it("extracts Java enum status types (no string values)", () => {
it('extracts Java enum status types (no string values)', () => {
const content = `
public enum OrderStatus { PENDING, ACTIVE, COMPLETED, CANCELLED }
`;
const types = extractStatusTypes(content, "java", "Order.java");
const types = extractStatusTypes(content, 'java', 'Order.java');
expect(types).toHaveLength(1);
expect(types[0]).toMatchObject({
name: "OrderStatus",
values: ["PENDING", "ACTIVE", "COMPLETED", "CANCELLED"],
kind: "enum",
filePath: "Order.java",
name: 'OrderStatus',
values: ['PENDING', 'ACTIVE', 'COMPLETED', 'CANCELLED'],
kind: 'enum',
filePath: 'Order.java',
});
});
});
describe("extractStatusTransitions", () => {
it("detects Prisma status update patterns", () => {
describe('extractStatusTransitions', () => {
it('detects Prisma status update patterns', () => {
const content = `
async function approveGrant(grantId: string) {
await prisma.grant.update({
@ -107,21 +100,17 @@ async function approveGrant(grantId: string) {
});
}
`;
const transitions = extractStatusTransitions(
content,
"typescript",
"engine.ts",
);
const transitions = extractStatusTransitions(content, 'typescript', 'engine.ts');
expect(transitions).toHaveLength(1);
expect(transitions[0]).toMatchObject({
toStatus: "ACTIVE",
entityType: "grant",
functionName: "approveGrant",
filePath: "engine.ts",
toStatus: 'ACTIVE',
entityType: 'grant',
functionName: 'approveGrant',
filePath: 'engine.ts',
});
});
it("detects conditional status transitions with fromStatus", () => {
it('detects conditional status transitions with fromStatus', () => {
const content = `
async function submitGrant(grant: Grant) {
if (grant.status === 'DRAFT') {
@ -132,19 +121,15 @@ async function submitGrant(grant: Grant) {
}
}
`;
const transitions = extractStatusTransitions(
content,
"typescript",
"route.ts",
);
const transitions = extractStatusTransitions(content, 'typescript', 'route.ts');
expect(transitions).toHaveLength(1);
expect(transitions[0]).toMatchObject({
fromStatus: "DRAFT",
toStatus: "ACTIVE",
fromStatus: 'DRAFT',
toStatus: 'ACTIVE',
});
});
it("detects transactional status updates", () => {
it('detects transactional status updates', () => {
const content = `
await prisma.$transaction(async (tx) => {
await tx.approvalStepInstance.update({
@ -153,102 +138,82 @@ await prisma.$transaction(async (tx) => {
});
});
`;
const transitions = extractStatusTransitions(
content,
"typescript",
"engine.ts",
);
const transitions = extractStatusTransitions(content, 'typescript', 'engine.ts');
expect(transitions).toHaveLength(1);
expect(transitions[0]).toMatchObject({
toStatus: "approved",
entityType: "approvalStepInstance",
toStatus: 'approved',
entityType: 'approvalStepInstance',
isTransactional: true,
});
});
it("detects direct assignment transitions", () => {
it('detects direct assignment transitions', () => {
const content = `
function shipOrder(order) {
order.status = 'shipped';
}
`;
const transitions = extractStatusTransitions(
content,
"javascript",
"order.js",
);
const transitions = extractStatusTransitions(content, 'javascript', 'order.js');
expect(transitions).toHaveLength(1);
expect(transitions[0]).toMatchObject({
toStatus: "shipped",
entityType: "order",
functionName: "shipOrder",
toStatus: 'shipped',
entityType: 'order',
functionName: 'shipOrder',
});
});
it("detects setter pattern transitions", () => {
it('detects setter pattern transitions', () => {
const content = `
function approve(entity) {
entity.setStatus('approved');
}
`;
const transitions = extractStatusTransitions(
content,
"typescript",
"service.ts",
);
const transitions = extractStatusTransitions(content, 'typescript', 'service.ts');
expect(transitions).toHaveLength(1);
expect(transitions[0]).toMatchObject({
toStatus: "approved",
entityType: "entity",
toStatus: 'approved',
entityType: 'entity',
});
});
it("detects Python self.status assignment", () => {
it('detects Python self.status assignment', () => {
const content = `
def activate(self):
self.status = 'active'
`;
const transitions = extractStatusTransitions(content, "python", "model.py");
const transitions = extractStatusTransitions(content, 'python', 'model.py');
expect(transitions).toHaveLength(1);
expect(transitions[0]).toMatchObject({
toStatus: "active",
entityType: "self",
filePath: "model.py",
toStatus: 'active',
entityType: 'self',
filePath: 'model.py',
});
});
it("detects generic .update() with status field", () => {
it('detects generic .update() with status field', () => {
const content = `
function completeTask(task) {
task.update({ status: 'completed', finishedAt: new Date() });
}
`;
const transitions = extractStatusTransitions(
content,
"javascript",
"task.js",
);
const transitions = extractStatusTransitions(content, 'javascript', 'task.js');
expect(transitions).toHaveLength(1);
expect(transitions[0]).toMatchObject({
toStatus: "completed",
entityType: "task",
toStatus: 'completed',
entityType: 'task',
});
});
it("detects set_status snake_case setter", () => {
it('detects set_status snake_case setter', () => {
const content = `
def process(order):
order.set_status('processing')
`;
const transitions = extractStatusTransitions(
content,
"python",
"service.py",
);
const transitions = extractStatusTransitions(content, 'python', 'service.py');
expect(transitions).toHaveLength(1);
expect(transitions[0]).toMatchObject({
toStatus: "processing",
entityType: "order",
toStatus: 'processing',
entityType: 'order',
});
});
});

13
package-lock.json generated
View file

@ -14,8 +14,8 @@
"eslint-plugin-unused-imports": "^4.4.1",
"husky": "^9.1.7",
"lint-staged": "^15.5.0",
"prettier": "^3.8.0",
"prettier-plugin-tailwindcss": "^0.7.0"
"prettier": "^3.8.1",
"prettier-plugin-tailwindcss": "^0.7.2"
}
},
"node_modules/@babel/code-frame": {
@ -49,6 +49,7 @@
"integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/code-frame": "^7.29.0",
"@babel/generator": "^7.29.0",
@ -603,6 +604,7 @@
"integrity": "sha512-NZZgp0Fm2IkD+La5PR81sd+g+8oS6JwJje+aRWsDocxHkjyRw0J5L5ZTlN3LI1LlOcGL7ph3eaIUmTXMIjLk0w==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@eslint-community/regexpp": "^4.12.2",
"@typescript-eslint/scope-manager": "8.57.2",
@ -632,6 +634,7 @@
"integrity": "sha512-30ScMRHIAD33JJQkgfGW1t8CURZtjc2JpTrq5n2HFhOefbAhb7ucc7xJwdWcrEtqUIYJ73Nybpsggii6GtAHjA==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@typescript-eslint/scope-manager": "8.57.2",
"@typescript-eslint/types": "8.57.2",
@ -836,6 +839,7 @@
"integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==",
"dev": true,
"license": "MIT",
"peer": true,
"bin": {
"acorn": "bin/acorn"
},
@ -988,6 +992,7 @@
}
],
"license": "MIT",
"peer": true,
"dependencies": {
"baseline-browser-mapping": "^2.9.0",
"caniuse-lite": "^1.0.30001759",
@ -1226,6 +1231,7 @@
"integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.8.0",
"@eslint-community/regexpp": "^4.12.1",
@ -2357,6 +2363,7 @@
"integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==",
"dev": true,
"license": "MIT",
"peer": true,
"bin": {
"prettier": "bin/prettier.cjs"
},
@ -2696,6 +2703,7 @@
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
"dev": true,
"license": "MIT",
"peer": true,
"engines": {
"node": ">=12"
},
@ -2884,6 +2892,7 @@
"integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==",
"dev": true,
"license": "MIT",
"peer": true,
"funding": {
"url": "https://github.com/sponsors/colinhacks"
}

View file

@ -17,8 +17,8 @@
"eslint-plugin-unused-imports": "^4.4.1",
"husky": "^9.1.7",
"lint-staged": "^15.5.0",
"prettier": "^3.8.0",
"prettier-plugin-tailwindcss": "^0.7.0"
"prettier": "^3.8.1",
"prettier-plugin-tailwindcss": "^0.7.2"
},
"lint-staged": {
"*.{ts,tsx}": [