Initial commit: add .gitignore and README
Some checks failed
CI / lint-and-test (push) Has been cancelled

This commit is contained in:
defiQUG
2026-02-09 21:51:50 -08:00
commit 93df3c8c20
116 changed files with 10080 additions and 0 deletions

View File

@@ -0,0 +1,24 @@
{
"name": "@sankofa/workflow",
"version": "0.1.0",
"private": true,
"type": "module",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"scripts": {
"build": "tsc",
"dev": "tsx watch src/index.ts",
"test": "vitest run",
"lint": "eslint src --ext .ts"
},
"dependencies": {
"@sankofa/schema": "workspace:*"
},
"devDependencies": {
"@types/node": "^22.10.0",
"eslint": "^9.15.0",
"tsx": "^4.19.0",
"typescript": "^5.7.0",
"vitest": "^2.1.0"
}
}

View File

@@ -0,0 +1,12 @@
import { describe, it, expect } from "vitest";
import { canTransitionPO, computeOfferRiskScore } from "./index";
describe("workflow", () => {
it("allows draft to pending_approval", () => {
expect(canTransitionPO("draft", "pending_approval")).toBe(true);
});
it("risk score computed", () => {
const { score } = computeOfferRiskScore({ trustTier: "unknown" });
expect(score).toBeGreaterThanOrEqual(0);
});
});

View File

@@ -0,0 +1,39 @@
export const PO_STATUS = ["draft", "pending_approval", "approved", "rejected", "ordered", "received"] as const;
export type POStatus = (typeof PO_STATUS)[number];
export const APPROVAL_STAGES = ["requester", "procurement", "finance", "executive"] as const;
export type ApprovalStage = (typeof APPROVAL_STAGES)[number];
export function nextPOStage(current: ApprovalStage | null): ApprovalStage | null {
if (!current) return "requester";
const i = APPROVAL_STAGES.indexOf(current);
return i < APPROVAL_STAGES.length - 1 ? APPROVAL_STAGES[i + 1] : null;
}
export function canTransitionPO(from: POStatus, to: POStatus): boolean {
const allowed: Record<POStatus, POStatus[]> = {
draft: ["pending_approval", "rejected"],
pending_approval: ["approved", "rejected", "draft"],
approved: ["ordered"],
rejected: ["draft"],
ordered: ["received"],
received: [],
};
return allowed[from]?.includes(to) ?? false;
}
export interface RiskFactors {
trustTier: string;
priceDeviation?: number;
conditionAmbiguity?: boolean;
}
export function computeOfferRiskScore(factors: RiskFactors): { score: number; factors: RiskFactors } {
let score = 0;
if (factors.trustTier === "unknown") score += 30;
else if (factors.trustTier === "low") score += 20;
else if (factors.trustTier === "medium") score += 10;
if (factors.priceDeviation != null && factors.priceDeviation > 0.2) score += 25;
if (factors.conditionAmbiguity) score += 20;
return { score: Math.min(100, score), factors };
}

View File

@@ -0,0 +1,15 @@
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"strict": true,
"outDir": "dist",
"rootDir": "src",
"skipLibCheck": true,
"declaration": true,
"declarationMap": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}