mirror of
https://github.com/seemueller-io/sumpin.git
synced 2025-09-08 22:56:46 +00:00
add code
This commit is contained in:
252
lib/__tests__/v1.test.ts
Normal file
252
lib/__tests__/v1.test.ts
Normal file
@@ -0,0 +1,252 @@
|
||||
import { expect, test, describe } from "bun:test";
|
||||
import { ProfessionModel, Domain, Specialization, Role, Responsibility, Attribute } from "../v1.ts";
|
||||
|
||||
describe("V1 Professional Hierarchy Model", () => {
|
||||
describe("Attribute Model", () => {
|
||||
test("should create an attribute with required fields", () => {
|
||||
const attribute = Attribute.create({
|
||||
name: "JavaScript",
|
||||
type: "Skill"
|
||||
});
|
||||
|
||||
expect(attribute.name).toBe("JavaScript");
|
||||
expect(attribute.type).toBe("Skill");
|
||||
expect(attribute.description).toBe("");
|
||||
});
|
||||
|
||||
test("should create an attribute with description", () => {
|
||||
const attribute = Attribute.create({
|
||||
name: "React",
|
||||
type: "Tool",
|
||||
description: "Frontend library for building user interfaces"
|
||||
});
|
||||
|
||||
expect(attribute.name).toBe("React");
|
||||
expect(attribute.type).toBe("Tool");
|
||||
expect(attribute.description).toBe("Frontend library for building user interfaces");
|
||||
});
|
||||
|
||||
test("should accept all valid attribute types", () => {
|
||||
const skill = Attribute.create({ name: "Problem Solving", type: "Skill" });
|
||||
const tool = Attribute.create({ name: "VS Code", type: "Tool" });
|
||||
const trait = Attribute.create({ name: "Leadership", type: "Trait" });
|
||||
|
||||
expect(skill.type).toBe("Skill");
|
||||
expect(tool.type).toBe("Tool");
|
||||
expect(trait.type).toBe("Trait");
|
||||
});
|
||||
});
|
||||
|
||||
describe("Responsibility Model", () => {
|
||||
test("should create a responsibility with required attributes", () => {
|
||||
const jsAttribute = Attribute.create({ name: "JavaScript", type: "Skill" });
|
||||
const reactAttribute = Attribute.create({ name: "React", type: "Tool" });
|
||||
|
||||
const responsibility = Responsibility.create({
|
||||
title: "Build User Interfaces",
|
||||
outcome: "Functional and responsive web applications",
|
||||
requiredAttributes: [jsAttribute, reactAttribute]
|
||||
});
|
||||
|
||||
expect(responsibility.title).toBe("Build User Interfaces");
|
||||
expect(responsibility.outcome).toBe("Functional and responsive web applications");
|
||||
expect(responsibility.requiredAttributes).toHaveLength(2);
|
||||
expect(responsibility.requiredAttributes[0].name).toBe("JavaScript");
|
||||
expect(responsibility.requiredAttributes[1].name).toBe("React");
|
||||
});
|
||||
|
||||
test("should create a responsibility with empty attributes array", () => {
|
||||
const responsibility = Responsibility.create({
|
||||
title: "Code Review",
|
||||
outcome: "High quality code",
|
||||
requiredAttributes: []
|
||||
});
|
||||
|
||||
expect(responsibility.requiredAttributes).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Role Model", () => {
|
||||
test("should create a role with all seniority levels", () => {
|
||||
const seniorityLevels = ["Intern", "Junior", "Mid", "Senior", "Lead", "Principal"];
|
||||
|
||||
seniorityLevels.forEach(level => {
|
||||
const role = Role.create({
|
||||
title: `${level} Developer`,
|
||||
responsibilities: [],
|
||||
requiredAttributes: [],
|
||||
seniority: level as any
|
||||
});
|
||||
|
||||
expect(role.seniority).toBe(level);
|
||||
});
|
||||
});
|
||||
|
||||
test("should create a role with responsibilities and attributes", () => {
|
||||
const attributeForResponsibility = Attribute.create({ name: "TypeScript", type: "Skill" });
|
||||
const attributeForRole = Attribute.create({ name: "TypeScript", type: "Skill" });
|
||||
const responsibility = Responsibility.create({
|
||||
title: "Develop Features",
|
||||
outcome: "Working software features",
|
||||
requiredAttributes: [attributeForResponsibility]
|
||||
});
|
||||
|
||||
const role = Role.create({
|
||||
title: "Frontend Developer",
|
||||
responsibilities: [responsibility],
|
||||
requiredAttributes: [attributeForRole],
|
||||
seniority: "Mid"
|
||||
});
|
||||
|
||||
expect(role.title).toBe("Frontend Developer");
|
||||
expect(role.responsibilities).toHaveLength(1);
|
||||
expect(role.requiredAttributes).toHaveLength(1);
|
||||
expect(role.seniority).toBe("Mid");
|
||||
});
|
||||
});
|
||||
|
||||
describe("Specialization Model", () => {
|
||||
test("should create a specialization with roles and attributes", () => {
|
||||
const coreAttribute = Attribute.create({ name: "Web Development", type: "Skill" });
|
||||
const role = Role.create({
|
||||
title: "Web Developer",
|
||||
responsibilities: [],
|
||||
requiredAttributes: [],
|
||||
seniority: "Mid"
|
||||
});
|
||||
|
||||
const specialization = Specialization.create({
|
||||
name: "Frontend Development",
|
||||
focus: "User interface and experience",
|
||||
coreAttributes: [coreAttribute],
|
||||
roles: [role]
|
||||
});
|
||||
|
||||
expect(specialization.name).toBe("Frontend Development");
|
||||
expect(specialization.focus).toBe("User interface and experience");
|
||||
expect(specialization.coreAttributes).toHaveLength(1);
|
||||
expect(specialization.roles).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Domain Model", () => {
|
||||
test("should create a domain with specializations", () => {
|
||||
const attribute = Attribute.create({ name: "Programming", type: "Skill" });
|
||||
const specialization = Specialization.create({
|
||||
name: "Software Engineering",
|
||||
focus: "Building software systems",
|
||||
coreAttributes: [],
|
||||
roles: []
|
||||
});
|
||||
|
||||
const domain = Domain.create({
|
||||
name: "Technology",
|
||||
description: "Technology and software development",
|
||||
specializations: [specialization],
|
||||
coreAttributes: [attribute]
|
||||
});
|
||||
|
||||
expect(domain.name).toBe("Technology");
|
||||
expect(domain.description).toBe("Technology and software development");
|
||||
expect(domain.specializations).toHaveLength(1);
|
||||
expect(domain.coreAttributes).toHaveLength(1);
|
||||
});
|
||||
|
||||
test("should create a domain with empty description", () => {
|
||||
const domain = Domain.create({
|
||||
name: "Engineering",
|
||||
specializations: [],
|
||||
coreAttributes: []
|
||||
});
|
||||
|
||||
expect(domain.description).toBe("");
|
||||
});
|
||||
});
|
||||
|
||||
describe("ProfessionModel", () => {
|
||||
test("should create a profession model with domains", () => {
|
||||
const domain = Domain.create({
|
||||
name: "Healthcare",
|
||||
specializations: [],
|
||||
coreAttributes: []
|
||||
});
|
||||
|
||||
const professionModel = ProfessionModel.create({
|
||||
domains: [domain]
|
||||
});
|
||||
|
||||
expect(professionModel.domains).toHaveLength(1);
|
||||
expect(professionModel.domains[0].name).toBe("Healthcare");
|
||||
});
|
||||
|
||||
test("should create an empty profession model", () => {
|
||||
const professionModel = ProfessionModel.create({
|
||||
domains: []
|
||||
});
|
||||
|
||||
expect(professionModel.domains).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Complete Hierarchy Integration", () => {
|
||||
test("should create a complete professional hierarchy", () => {
|
||||
const jsSkillForResponsibility = Attribute.create({ name: "JavaScript", type: "Skill" });
|
||||
const reactToolForResponsibility = Attribute.create({ name: "React", type: "Tool" });
|
||||
|
||||
// Create attributes for role
|
||||
const jsSkillForRole = Attribute.create({ name: "JavaScript", type: "Skill" });
|
||||
const reactToolForRole = Attribute.create({ name: "React", type: "Tool" });
|
||||
const leadershipTrait = Attribute.create({ name: "Leadership", type: "Trait" });
|
||||
|
||||
const jsSkillForSpecialization = Attribute.create({ name: "JavaScript", type: "Skill" });
|
||||
const reactToolForSpecialization = Attribute.create({ name: "React", type: "Tool" });
|
||||
|
||||
const jsSkillForDomain = Attribute.create({ name: "JavaScript", type: "Skill" });
|
||||
|
||||
const responsibility = Responsibility.create({
|
||||
title: "Build React Applications",
|
||||
outcome: "Scalable web applications",
|
||||
requiredAttributes: [jsSkillForResponsibility, reactToolForResponsibility]
|
||||
});
|
||||
|
||||
// Create role
|
||||
const role = Role.create({
|
||||
title: "Senior Frontend Developer",
|
||||
responsibilities: [responsibility],
|
||||
requiredAttributes: [jsSkillForRole, reactToolForRole, leadershipTrait],
|
||||
seniority: "Senior"
|
||||
});
|
||||
|
||||
const specialization = Specialization.create({
|
||||
name: "Frontend Development",
|
||||
focus: "User interfaces and client-side applications",
|
||||
coreAttributes: [jsSkillForSpecialization, reactToolForSpecialization],
|
||||
roles: [role]
|
||||
});
|
||||
|
||||
// Create domain
|
||||
const domain = Domain.create({
|
||||
name: "Software Engineering",
|
||||
description: "Building software systems and applications",
|
||||
specializations: [specialization],
|
||||
coreAttributes: [jsSkillForDomain]
|
||||
});
|
||||
|
||||
const professionModel = ProfessionModel.create({
|
||||
domains: [domain]
|
||||
});
|
||||
|
||||
// Verify the complete hierarchy
|
||||
expect(professionModel.domains).toHaveLength(1);
|
||||
expect(professionModel.domains[0].specializations).toHaveLength(1);
|
||||
expect(professionModel.domains[0].specializations[0].roles).toHaveLength(1);
|
||||
expect(professionModel.domains[0].specializations[0].roles[0].responsibilities).toHaveLength(1);
|
||||
|
||||
const retrievedRole = professionModel.domains[0].specializations[0].roles[0];
|
||||
expect(retrievedRole.title).toBe("Senior Frontend Developer");
|
||||
expect(retrievedRole.seniority).toBe("Senior");
|
||||
expect(retrievedRole.responsibilities[0].title).toBe("Build React Applications");
|
||||
expect(retrievedRole.requiredAttributes).toHaveLength(3);
|
||||
});
|
||||
});
|
||||
});
|
476
lib/__tests__/v2.test.ts
Normal file
476
lib/__tests__/v2.test.ts
Normal file
@@ -0,0 +1,476 @@
|
||||
import { expect, test, describe, beforeEach } from "bun:test";
|
||||
import {
|
||||
TaskModel,
|
||||
RoleModel,
|
||||
FieldModel,
|
||||
ProfessionModel,
|
||||
IndustryModel,
|
||||
DomainModel,
|
||||
Enterprise,
|
||||
Task,
|
||||
Role,
|
||||
Field,
|
||||
Profession,
|
||||
Industry,
|
||||
Domain,
|
||||
IRootStore
|
||||
} from "../v2.ts";
|
||||
|
||||
describe("V2 Professional Hierarchy Model", () => {
|
||||
describe("TaskModel", () => {
|
||||
test("should create a task with UUID identifier", () => {
|
||||
const task = TaskModel.create({
|
||||
name: "Design REST endpoints"
|
||||
});
|
||||
|
||||
expect(task.name).toBe("Design REST endpoints");
|
||||
expect(task.id).toBeDefined();
|
||||
expect(typeof task.id).toBe("string");
|
||||
expect(task.id.length).toBeGreaterThan(0);
|
||||
expect(task.description).toBeUndefined();
|
||||
});
|
||||
|
||||
test("should create a task with description", () => {
|
||||
const task = TaskModel.create({
|
||||
name: "Write unit tests",
|
||||
description: "Create comprehensive test coverage for new features"
|
||||
});
|
||||
|
||||
expect(task.name).toBe("Write unit tests");
|
||||
expect(task.description).toBe("Create comprehensive test coverage for new features");
|
||||
});
|
||||
|
||||
test("should update task properties", () => {
|
||||
const task = TaskModel.create({
|
||||
name: "Initial task"
|
||||
});
|
||||
|
||||
task.update({
|
||||
name: "Updated task",
|
||||
description: "Updated description"
|
||||
});
|
||||
|
||||
expect(task.name).toBe("Updated task");
|
||||
expect(task.description).toBe("Updated description");
|
||||
});
|
||||
|
||||
test("should have unique IDs for different tasks", () => {
|
||||
const task1 = TaskModel.create({ name: "Task 1" });
|
||||
const task2 = TaskModel.create({ name: "Task 2" });
|
||||
|
||||
expect(task1.id).not.toBe(task2.id);
|
||||
});
|
||||
});
|
||||
|
||||
describe("RoleModel", () => {
|
||||
test("should create a role with tasks", () => {
|
||||
const role = RoleModel.create({
|
||||
title: "API Engineer",
|
||||
summary: "Designs and implements REST APIs"
|
||||
});
|
||||
|
||||
expect(role.title).toBe("API Engineer");
|
||||
expect(role.summary).toBe("Designs and implements REST APIs");
|
||||
expect(role.tasks).toHaveLength(0);
|
||||
expect(role.id).toBeDefined();
|
||||
});
|
||||
|
||||
test("should add and remove tasks", () => {
|
||||
const role = RoleModel.create({
|
||||
title: "Backend Developer"
|
||||
});
|
||||
|
||||
role.addTask({ name: "Design database schema" });
|
||||
role.addTask({ name: "Implement API endpoints" });
|
||||
|
||||
expect(role.tasks).toHaveLength(2);
|
||||
expect((role.tasks[0] as any).name).toBe("Design database schema");
|
||||
expect((role.tasks[1] as any).name ).toBe("Implement API endpoints");
|
||||
|
||||
// Remove a task
|
||||
const taskToRemove = role.tasks[0];
|
||||
role.removeTask(taskToRemove);
|
||||
|
||||
expect(role.tasks).toHaveLength(1);
|
||||
expect((role.tasks[0] as any).name).toBe("Implement API endpoints");
|
||||
});
|
||||
|
||||
test("should return all tasks through view", () => {
|
||||
const role = RoleModel.create({
|
||||
title: "Frontend Developer"
|
||||
});
|
||||
|
||||
role.addTask({ name: "Build components" });
|
||||
role.addTask({ name: "Write tests" });
|
||||
|
||||
const allTasks = role.allTasks;
|
||||
expect(allTasks).toHaveLength(2);
|
||||
expect(allTasks[0].name).toBe("Build components");
|
||||
expect(allTasks[1].name).toBe("Write tests");
|
||||
});
|
||||
});
|
||||
|
||||
describe("FieldModel", () => {
|
||||
test("should create a field with roles", () => {
|
||||
const field = FieldModel.create({
|
||||
name: "Backend Development",
|
||||
description: "Server-side application development"
|
||||
});
|
||||
|
||||
expect(field.name).toBe("Backend Development");
|
||||
expect(field.description).toBe("Server-side application development");
|
||||
expect(field.roles).toHaveLength(0);
|
||||
});
|
||||
|
||||
test("should add and remove roles", () => {
|
||||
const field = FieldModel.create({
|
||||
name: "Frontend Development"
|
||||
});
|
||||
|
||||
field.addRole({ title: "React Developer" });
|
||||
field.addRole({ title: "Vue Developer" });
|
||||
|
||||
expect(field.roles).toHaveLength(2);
|
||||
expect(field.roles[0].title).toBe("React Developer");
|
||||
expect(field.roles[1].title).toBe("Vue Developer");
|
||||
|
||||
const roleToRemove = field.roles[0];
|
||||
field.removeRole(roleToRemove);
|
||||
|
||||
expect(field.roles).toHaveLength(1);
|
||||
expect(field.roles[0].title).toBe("Vue Developer");
|
||||
});
|
||||
|
||||
test("should return all tasks from nested roles", () => {
|
||||
const field = FieldModel.create({
|
||||
name: "Full Stack Development"
|
||||
});
|
||||
|
||||
field.addRole({ title: "Frontend Developer" });
|
||||
field.addRole({ title: "Backend Developer" });
|
||||
|
||||
// Add tasks to roles
|
||||
field.roles[0].addTask({ name: "Build UI components" });
|
||||
field.roles[0].addTask({ name: "Handle user interactions" });
|
||||
field.roles[1].addTask({ name: "Design APIs" });
|
||||
|
||||
const allTasks = field.allTasks;
|
||||
expect(allTasks).toHaveLength(3);
|
||||
expect(allTasks.map(t => t.name)).toContain("Build UI components");
|
||||
expect(allTasks.map(t => t.name)).toContain("Handle user interactions");
|
||||
expect(allTasks.map(t => t.name)).toContain("Design APIs");
|
||||
});
|
||||
});
|
||||
|
||||
describe("ProfessionModel", () => {
|
||||
test("should create a profession with fields", () => {
|
||||
const profession = ProfessionModel.create({
|
||||
name: "Software Engineering",
|
||||
description: "Building software systems and applications"
|
||||
});
|
||||
|
||||
expect(profession.name).toBe("Software Engineering");
|
||||
expect(profession.description).toBe("Building software systems and applications");
|
||||
expect(profession.fields).toHaveLength(0);
|
||||
});
|
||||
|
||||
test("should add and remove fields", () => {
|
||||
const profession = ProfessionModel.create({
|
||||
name: "Web Development"
|
||||
});
|
||||
|
||||
profession.addField({ name: "Frontend" });
|
||||
profession.addField({ name: "Backend" });
|
||||
|
||||
expect(profession.fields).toHaveLength(2);
|
||||
expect(profession.fields[0].name).toBe("Frontend");
|
||||
expect(profession.fields[1].name).toBe("Backend");
|
||||
|
||||
const fieldToRemove = profession.fields[0];
|
||||
profession.removeField(fieldToRemove);
|
||||
|
||||
expect(profession.fields).toHaveLength(1);
|
||||
expect(profession.fields[0].name).toBe("Backend");
|
||||
});
|
||||
|
||||
test("should return all tasks from nested hierarchy", () => {
|
||||
const profession = ProfessionModel.create({
|
||||
name: "Software Engineering"
|
||||
});
|
||||
|
||||
profession.addField({ name: "Backend" });
|
||||
profession.fields[0].addRole({ title: "API Developer" });
|
||||
profession.fields[0].roles[0].addTask({ name: "Design REST endpoints" });
|
||||
profession.fields[0].roles[0].addTask({ name: "Implement authentication" });
|
||||
|
||||
const allTasks = profession.allTasks;
|
||||
expect(allTasks).toHaveLength(2);
|
||||
expect(allTasks.map(t => t.name)).toContain("Design REST endpoints");
|
||||
expect(allTasks.map(t => t.name)).toContain("Implement authentication");
|
||||
});
|
||||
});
|
||||
|
||||
describe("IndustryModel", () => {
|
||||
test("should create an industry with professions", () => {
|
||||
const industry = IndustryModel.create({
|
||||
name: "Software",
|
||||
description: "Software development and technology"
|
||||
});
|
||||
|
||||
expect(industry.name).toBe("Software");
|
||||
expect(industry.description).toBe("Software development and technology");
|
||||
expect(industry.professions).toHaveLength(0);
|
||||
});
|
||||
|
||||
test("should add and remove professions", () => {
|
||||
const industry = IndustryModel.create({
|
||||
name: "Technology"
|
||||
});
|
||||
|
||||
industry.addProfession({ name: "Software Engineering" });
|
||||
industry.addProfession({ name: "Data Science" });
|
||||
|
||||
expect(industry.professions).toHaveLength(2);
|
||||
expect(industry.professions[0].name).toBe("Software Engineering");
|
||||
expect(industry.professions[1].name).toBe("Data Science");
|
||||
|
||||
// Remove a profession
|
||||
const professionToRemove = industry.professions[0];
|
||||
industry.removeProfession(professionToRemove);
|
||||
|
||||
expect(industry.professions).toHaveLength(1);
|
||||
expect(industry.professions[0].name).toBe("Data Science");
|
||||
});
|
||||
|
||||
test("should return all tasks from nested hierarchy", () => {
|
||||
const industry = IndustryModel.create({
|
||||
name: "Software"
|
||||
});
|
||||
|
||||
industry.addProfession({ name: "Web Development" });
|
||||
industry.professions[0].addField({ name: "Frontend" });
|
||||
industry.professions[0].fields[0].addRole({ title: "React Developer" });
|
||||
industry.professions[0].fields[0].roles[0].addTask({ name: "Build components" });
|
||||
industry.professions[0].fields[0].roles[0].addTask({ name: "Manage state" });
|
||||
|
||||
const allTasks = industry.allTasks;
|
||||
expect(allTasks).toHaveLength(2);
|
||||
expect(allTasks.map(t => t.name)).toContain("Build components");
|
||||
expect(allTasks.map(t => t.name)).toContain("Manage state");
|
||||
});
|
||||
});
|
||||
|
||||
describe("DomainModel", () => {
|
||||
test("should create a domain with industries", () => {
|
||||
const domain = DomainModel.create({
|
||||
name: "STEM",
|
||||
description: "Science, Technology, Engineering, and Mathematics"
|
||||
});
|
||||
|
||||
expect(domain.name).toBe("STEM");
|
||||
expect(domain.description).toBe("Science, Technology, Engineering, and Mathematics");
|
||||
expect(domain.industries).toHaveLength(0);
|
||||
});
|
||||
|
||||
test("should add and remove industries", () => {
|
||||
const domain = DomainModel.create({
|
||||
name: "Technology"
|
||||
});
|
||||
|
||||
domain.addIndustry({ name: "Software" });
|
||||
domain.addIndustry({ name: "Hardware" });
|
||||
|
||||
expect(domain.industries).toHaveLength(2);
|
||||
expect(domain.industries[0].name).toBe("Software");
|
||||
expect(domain.industries[1].name).toBe("Hardware");
|
||||
|
||||
const industryToRemove = domain.industries[0];
|
||||
domain.removeIndustry(industryToRemove);
|
||||
|
||||
expect(domain.industries).toHaveLength(1);
|
||||
expect(domain.industries[0].name).toBe("Hardware");
|
||||
});
|
||||
|
||||
test("should return all tasks from nested hierarchy", () => {
|
||||
const domain = DomainModel.create({
|
||||
name: "STEM"
|
||||
});
|
||||
|
||||
domain.addIndustry({ name: "Software" });
|
||||
domain.industries[0].addProfession({ name: "Software Engineering" });
|
||||
domain.industries[0].professions[0].addField({ name: "Backend" });
|
||||
domain.industries[0].professions[0].fields[0].addRole({ title: "API Engineer" });
|
||||
domain.industries[0].professions[0].fields[0].roles[0].addTask({ name: "Design REST endpoints" });
|
||||
|
||||
const allTasks = domain.allTasks;
|
||||
expect(allTasks).toHaveLength(1);
|
||||
expect(allTasks[0].name).toBe("Design REST endpoints");
|
||||
});
|
||||
});
|
||||
|
||||
describe("Enterprise", () => {
|
||||
let store: IRootStore;
|
||||
|
||||
beforeEach(() => {
|
||||
store = Enterprise.create({});
|
||||
});
|
||||
|
||||
test("should create an empty root store", () => {
|
||||
expect(store.domains).toHaveLength(0);
|
||||
});
|
||||
|
||||
test("should add domains", () => {
|
||||
store.addDomain({ name: "STEM" });
|
||||
store.addDomain({ name: "Arts" });
|
||||
|
||||
expect(store.domains).toHaveLength(2);
|
||||
expect(store.domains[0].name).toBe("STEM");
|
||||
expect(store.domains[1].name).toBe("Arts");
|
||||
});
|
||||
|
||||
test("should return all tasks from entire hierarchy", () => {
|
||||
store.addDomain({ name: "STEM" });
|
||||
store.domains[0].addIndustry({ name: "Software" });
|
||||
store.domains[0].industries[0].addProfession({ name: "Software Engineering" });
|
||||
store.domains[0].industries[0].professions[0].addField({ name: "Backend" });
|
||||
store.domains[0].industries[0].professions[0].fields[0].addRole({ title: "API Engineer" });
|
||||
store.domains[0].industries[0].professions[0].fields[0].roles[0].addTask({ name: "Design REST endpoints" });
|
||||
store.domains[0].industries[0].professions[0].fields[0].roles[0].addTask({ name: "Implement authentication" });
|
||||
|
||||
const allTasks = store.allTasks;
|
||||
expect(allTasks).toHaveLength(2);
|
||||
expect(allTasks.map(t => t.name)).toContain("Design REST endpoints");
|
||||
expect(allTasks.map(t => t.name)).toContain("Implement authentication");
|
||||
});
|
||||
});
|
||||
|
||||
describe("Complete Hierarchy Integration", () => {
|
||||
test("should create and manipulate a complete 6-layer hierarchy", () => {
|
||||
const store = Enterprise.create({});
|
||||
|
||||
// Build the complete hierarchy as shown in the example
|
||||
store.addDomain({ name: "STEM" });
|
||||
store.domains[0].addIndustry({ name: "Software" });
|
||||
store.domains[0].industries[0].addProfession({ name: "Software Engineering" });
|
||||
store.domains[0].industries[0].professions[0].addField({ name: "Backend" });
|
||||
store.domains[0].industries[0].professions[0].fields[0].addRole({ title: "API Engineer" });
|
||||
store.domains[0].industries[0].professions[0].fields[0].roles[0].addTask({ name: "Design REST endpoints" });
|
||||
|
||||
expect(store.domains).toHaveLength(1);
|
||||
expect(store.domains[0].industries).toHaveLength(1);
|
||||
expect(store.domains[0].industries[0].professions).toHaveLength(1);
|
||||
expect(store.domains[0].industries[0].professions[0].fields).toHaveLength(1);
|
||||
expect(store.domains[0].industries[0].professions[0].fields[0].roles).toHaveLength(1);
|
||||
expect(store.domains[0].industries[0].professions[0].fields[0].roles[0].tasks).toHaveLength(1);
|
||||
|
||||
// Verify data integrity through the hierarchy
|
||||
const task = store.domains[0].industries[0].professions[0].fields[0].roles[0].tasks[0];
|
||||
expect(task.name).toBe("Design REST endpoints");
|
||||
|
||||
expect(store.allTasks).toHaveLength(1);
|
||||
expect(store.domains[0].allTasks).toHaveLength(1);
|
||||
expect(store.domains[0].industries[0].allTasks).toHaveLength(1);
|
||||
expect(store.domains[0].industries[0].professions[0].allTasks).toHaveLength(1);
|
||||
expect(store.domains[0].industries[0].professions[0].fields[0].allTasks).toHaveLength(1);
|
||||
|
||||
// Add more tasks and verify aggregation
|
||||
store.domains[0].industries[0].professions[0].fields[0].roles[0].addTask({ name: "Implement authentication" });
|
||||
store.domains[0].industries[0].professions[0].fields[0].addRole({ title: "Database Engineer" });
|
||||
store.domains[0].industries[0].professions[0].fields[0].roles[1].addTask({ name: "Design database schema" });
|
||||
|
||||
expect(store.allTasks).toHaveLength(3);
|
||||
expect(store.domains[0].industries[0].professions[0].fields[0].allTasks).toHaveLength(3);
|
||||
});
|
||||
|
||||
test("should handle multiple parallel hierarchies", () => {
|
||||
const store = Enterprise.create({});
|
||||
|
||||
store.addDomain({ name: "STEM" });
|
||||
store.domains[0].addIndustry({ name: "Software" });
|
||||
store.domains[0].industries[0].addProfession({ name: "Web Development" });
|
||||
store.domains[0].industries[0].professions[0].addField({ name: "Frontend" });
|
||||
store.domains[0].industries[0].professions[0].fields[0].addRole({ title: "React Developer" });
|
||||
store.domains[0].industries[0].professions[0].fields[0].roles[0].addTask({ name: "Build components" });
|
||||
|
||||
// Create second hierarchy branch
|
||||
store.addDomain({ name: "Arts" });
|
||||
store.domains[1].addIndustry({ name: "Digital Media" });
|
||||
store.domains[1].industries[0].addProfession({ name: "Graphic Design" });
|
||||
store.domains[1].industries[0].professions[0].addField({ name: "Web Design" });
|
||||
store.domains[1].industries[0].professions[0].fields[0].addRole({ title: "UI Designer" });
|
||||
store.domains[1].industries[0].professions[0].fields[0].roles[0].addTask({ name: "Create mockups" });
|
||||
|
||||
expect(store.domains).toHaveLength(2);
|
||||
expect(store.allTasks).toHaveLength(2);
|
||||
expect(store.allTasks.map(t => t.name)).toContain("Build components");
|
||||
expect(store.allTasks.map(t => t.name)).toContain("Create mockups");
|
||||
|
||||
// Verify each domain has its own tasks
|
||||
expect(store.domains[0].allTasks).toHaveLength(1);
|
||||
expect(store.domains[1].allTasks).toHaveLength(1);
|
||||
expect(store.domains[0].allTasks[0].name).toBe("Build components");
|
||||
expect(store.domains[1].allTasks[0].name).toBe("Create mockups");
|
||||
});
|
||||
});
|
||||
|
||||
describe("CRUD Operations", () => {
|
||||
test("should support task updates and removal", () => {
|
||||
const role = RoleModel.create({ title: "Developer" });
|
||||
role.addTask({ name: "Initial task", description: "Initial description" });
|
||||
|
||||
const task = role.tasks[0];
|
||||
const originalId = task.id;
|
||||
|
||||
task.update({ name: "Updated task", description: "Updated description" });
|
||||
expect(task.name).toBe("Updated task");
|
||||
expect(task.description).toBe("Updated description");
|
||||
expect(task.id).toBe(originalId); // ID should remain the same
|
||||
|
||||
// Remove task through parent
|
||||
expect(role.tasks).toHaveLength(1);
|
||||
role.removeTask(task);
|
||||
expect(role.tasks).toHaveLength(0);
|
||||
});
|
||||
|
||||
test("should support role removal", () => {
|
||||
const field = FieldModel.create({ name: "Development" });
|
||||
field.addRole({ title: "Developer" });
|
||||
|
||||
expect(field.roles).toHaveLength(1);
|
||||
const role = field.roles[0];
|
||||
field.removeRole(role);
|
||||
expect(field.roles).toHaveLength(0);
|
||||
});
|
||||
|
||||
test("should support field removal", () => {
|
||||
const profession = ProfessionModel.create({ name: "Engineering" });
|
||||
profession.addField({ name: "Software" });
|
||||
|
||||
expect(profession.fields).toHaveLength(1);
|
||||
const field = profession.fields[0];
|
||||
profession.removeField(field);
|
||||
expect(profession.fields).toHaveLength(0);
|
||||
});
|
||||
|
||||
test("should support profession removal", () => {
|
||||
const industry = IndustryModel.create({ name: "Tech" });
|
||||
industry.addProfession({ name: "Software Engineering" });
|
||||
|
||||
expect(industry.professions).toHaveLength(1);
|
||||
const profession = industry.professions[0];
|
||||
industry.removeProfession(profession);
|
||||
expect(industry.professions).toHaveLength(0);
|
||||
});
|
||||
|
||||
test("should support industry removal", () => {
|
||||
const domain = DomainModel.create({ name: "STEM" });
|
||||
domain.addIndustry({ name: "Software" });
|
||||
|
||||
expect(domain.industries).toHaveLength(1);
|
||||
const industry = domain.industries[0];
|
||||
domain.removeIndustry(industry);
|
||||
expect(domain.industries).toHaveLength(0);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
203
lib/agent-wrapper.ts
Normal file
203
lib/agent-wrapper.ts
Normal file
@@ -0,0 +1,203 @@
|
||||
import { Agent, StreamedRunResult } from '@openai/agents';
|
||||
import HierarchyGenerator, { GenerationParams } from './components/hierarchy-generator';
|
||||
import TemplateManager, { DomainTemplate } from './components/template-manager';
|
||||
import OutputFormatter, { OutputOptions, FormattedOutput } from './components/output-formatter';
|
||||
|
||||
export interface AgentConfig {
|
||||
name: string;
|
||||
instructions: string;
|
||||
model?: string;
|
||||
}
|
||||
|
||||
export interface HierarchyGenerationOptions extends GenerationParams {
|
||||
outputFormat?: OutputOptions;
|
||||
templateKey?: string;
|
||||
stream?: boolean;
|
||||
}
|
||||
|
||||
export class HierarchyAgent {
|
||||
private agent: Agent;
|
||||
private generator: HierarchyGenerator;
|
||||
private templateManager: TemplateManager;
|
||||
private outputFormatter: OutputFormatter;
|
||||
|
||||
constructor(config: AgentConfig) {
|
||||
this.agent = new Agent({
|
||||
name: config.name,
|
||||
instructions: config.instructions,
|
||||
model: config.model || 'gpt-4o-mini'
|
||||
});
|
||||
|
||||
this.generator = new HierarchyGenerator(this.agent);
|
||||
this.templateManager = new TemplateManager();
|
||||
this.outputFormatter = new OutputFormatter();
|
||||
}
|
||||
|
||||
async generateHierarchy(options: HierarchyGenerationOptions): Promise<FormattedOutput | StreamedRunResult> {
|
||||
let template: DomainTemplate;
|
||||
|
||||
if (options.templateKey) {
|
||||
template = this.templateManager.getTemplate(options.templateKey);
|
||||
if (!template) {
|
||||
throw new Error(`Template not found: ${options.templateKey}`);
|
||||
}
|
||||
} else {
|
||||
// Create a default template for the domain
|
||||
const version = options.version || 'v2';
|
||||
template = this.templateManager.createCustomTemplate(
|
||||
`${options.domain}-${version}`,
|
||||
options.domain,
|
||||
version
|
||||
);
|
||||
}
|
||||
|
||||
const generationParams: GenerationParams = {
|
||||
domain: options.domain,
|
||||
complexity: options.complexity || 'medium',
|
||||
includeSkills: options.includeSkills ?? true,
|
||||
includeTools: options.includeTools ?? true,
|
||||
includeExamples: options.includeExamples ?? true,
|
||||
stream: options.stream
|
||||
};
|
||||
|
||||
const content = await this.generator.generateFromTemplate(template, generationParams);
|
||||
|
||||
// If streaming, return the stream directly
|
||||
if (options.stream && content instanceof Object && 'toStream' in content) {
|
||||
return content as StreamedRunResult;
|
||||
}
|
||||
|
||||
const outputOptions = options.outputFormat || this.outputFormatter.getDefaultOptions();
|
||||
const formattedOutput = this.outputFormatter.formatOutput(
|
||||
content as string,
|
||||
options.domain,
|
||||
template.version,
|
||||
outputOptions
|
||||
);
|
||||
|
||||
return formattedOutput;
|
||||
}
|
||||
|
||||
// New method for streaming with enhanced visibility
|
||||
async generateHierarchyWithStreaming(
|
||||
options: HierarchyGenerationOptions,
|
||||
onStreamEvent?: (event: any) => void
|
||||
): Promise<FormattedOutput> {
|
||||
let template: DomainTemplate;
|
||||
|
||||
if (options.templateKey) {
|
||||
template = this.templateManager.getTemplate(options.templateKey);
|
||||
if (!template) {
|
||||
throw new Error(`Template not found: ${options.templateKey}`);
|
||||
}
|
||||
} else {
|
||||
// Create a default template for the domain
|
||||
const version = options.version || 'v2';
|
||||
template = this.templateManager.createCustomTemplate(
|
||||
`${options.domain}-${version}`,
|
||||
options.domain,
|
||||
version
|
||||
);
|
||||
}
|
||||
|
||||
const generationParams: GenerationParams = {
|
||||
domain: options.domain,
|
||||
complexity: options.complexity || 'medium',
|
||||
includeSkills: options.includeSkills ?? true,
|
||||
includeTools: options.includeTools ?? true,
|
||||
includeExamples: options.includeExamples ?? true
|
||||
};
|
||||
|
||||
const content = await this.generator.generateFromTemplateWithStreaming(
|
||||
template,
|
||||
generationParams,
|
||||
onStreamEvent
|
||||
);
|
||||
|
||||
// Format the output
|
||||
const outputOptions = options.outputFormat || this.outputFormatter.getDefaultOptions();
|
||||
const formattedOutput = this.outputFormatter.formatOutput(
|
||||
content,
|
||||
options.domain,
|
||||
template.version,
|
||||
outputOptions
|
||||
);
|
||||
|
||||
return formattedOutput;
|
||||
}
|
||||
|
||||
async generateExample(
|
||||
domain: string,
|
||||
version: 'v1' | 'v2' = 'v2',
|
||||
complexity: 'simple' | 'medium' | 'complex' = 'medium'
|
||||
): Promise<FormattedOutput> {
|
||||
return this.generateHierarchy({
|
||||
domain,
|
||||
version,
|
||||
complexity,
|
||||
includeSkills: true,
|
||||
includeTools: true,
|
||||
includeExamples: true
|
||||
});
|
||||
}
|
||||
|
||||
// Template management methods
|
||||
getAvailableTemplates(): DomainTemplate[] {
|
||||
return this.templateManager.getAllTemplates();
|
||||
}
|
||||
|
||||
getTemplatesByDomain(domain: string): DomainTemplate[] {
|
||||
return this.templateManager.getTemplatesByDomain(domain);
|
||||
}
|
||||
|
||||
getTemplatesByVersion(version: 'v1' | 'v2'): DomainTemplate[] {
|
||||
return this.templateManager.getTemplatesByVersion(version);
|
||||
}
|
||||
|
||||
addCustomTemplate(key: string, template: DomainTemplate): void {
|
||||
this.templateManager.addTemplate(key, template);
|
||||
}
|
||||
|
||||
// Validation and optimization methods
|
||||
async validateHierarchy(hierarchyData: any): Promise<boolean> {
|
||||
// TODO: Implement validation logic using the agent
|
||||
// Could validate structure, naming conventions, completeness, etc.
|
||||
return true;
|
||||
}
|
||||
|
||||
async optimizeHierarchy(hierarchyData: any): Promise<any> {
|
||||
// TODO: Implement optimization logic using the agent
|
||||
// Could suggest improvements, fill gaps, optimize structure, etc.
|
||||
return hierarchyData;
|
||||
}
|
||||
|
||||
// Batch generation methods
|
||||
async generateMultipleExamples(
|
||||
domains: string[],
|
||||
version: 'v1' | 'v2' = 'v2',
|
||||
outputFormat?: OutputOptions
|
||||
): Promise<FormattedOutput[]> {
|
||||
const results: FormattedOutput[] = [];
|
||||
|
||||
for (const domain of domains) {
|
||||
try {
|
||||
const result = await this.generateHierarchy({
|
||||
domain,
|
||||
version,
|
||||
complexity: 'medium',
|
||||
includeSkills: true,
|
||||
includeTools: true,
|
||||
includeExamples: true,
|
||||
outputFormat
|
||||
});
|
||||
results.push(result);
|
||||
} catch (error) {
|
||||
console.error(`Error generating example for ${domain}:`, error);
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
}
|
||||
|
||||
export default HierarchyAgent;
|
115
lib/components/hierarchy-generator.ts
Normal file
115
lib/components/hierarchy-generator.ts
Normal file
@@ -0,0 +1,115 @@
|
||||
import { Agent, run, StreamedRunResult } from '@openai/agents';
|
||||
|
||||
export interface HierarchyTemplate {
|
||||
version: 'v1' | 'v2';
|
||||
structure: string[];
|
||||
description: string;
|
||||
}
|
||||
|
||||
export interface GenerationParams {
|
||||
domain: string;
|
||||
complexity: 'simple' | 'medium' | 'complex';
|
||||
includeSkills: boolean;
|
||||
includeTools: boolean;
|
||||
includeExamples: boolean;
|
||||
stream?: boolean;
|
||||
}
|
||||
|
||||
export class HierarchyGenerator {
|
||||
private agent: Agent;
|
||||
|
||||
constructor(agent: Agent) {
|
||||
this.agent = agent;
|
||||
}
|
||||
|
||||
async generateFromTemplate(template: HierarchyTemplate, params: GenerationParams): Promise<string | StreamedRunResult> {
|
||||
const prompt = this.buildPrompt(template, params);
|
||||
|
||||
if (params.stream) {
|
||||
return await run(this.agent, prompt, { stream: true });
|
||||
} else {
|
||||
const result = await run(this.agent, prompt);
|
||||
return result.finalOutput;
|
||||
}
|
||||
}
|
||||
|
||||
// Helper method to stream and collect final output
|
||||
async generateFromTemplateWithStreaming(
|
||||
template: HierarchyTemplate,
|
||||
params: GenerationParams,
|
||||
onStreamEvent?: (event: any) => void
|
||||
): Promise<string> {
|
||||
const prompt = this.buildPrompt(template, params);
|
||||
console.log('🔄 Starting hierarchy generation...');
|
||||
|
||||
const stream = await run(this.agent, prompt, { stream: true });
|
||||
let content = '';
|
||||
|
||||
for await (const event of stream) {
|
||||
if (event.type === 'raw_model_stream_event' && event.data.delta) {
|
||||
// console.log(event.data.delta)
|
||||
content += event.delta;
|
||||
process.stdout.write(event.data.delta);
|
||||
}
|
||||
|
||||
if (event.type === 'agent_updated_stream_event') {
|
||||
console.log(`\n📝 Agent: ${event.agent.name} is processing...`);
|
||||
} else if (event.type === 'run_item_stream_event') {
|
||||
if (event.item.type === 'tool_call_item') {
|
||||
console.log('\n🔧 Tool being called...');
|
||||
} else if (event.item.type === 'message_output_item') {
|
||||
console.log('\n💬 Generating response...');
|
||||
}
|
||||
}
|
||||
|
||||
// Allow custom event handling
|
||||
if (onStreamEvent) {
|
||||
onStreamEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
console.log('\n✅ Hierarchy generation complete!');
|
||||
return stream.finalOutput;
|
||||
}
|
||||
|
||||
private buildPrompt(template: HierarchyTemplate, params: GenerationParams): string {
|
||||
const structureDescription = template.version === 'v1'
|
||||
? 'Domain → Specialization → Role → Responsibility'
|
||||
: 'Domain → Industry → Profession → Field → Role → Task';
|
||||
|
||||
const importStatement = template.version === 'v1'
|
||||
? 'import { Enterprise, DomainModel, SpecializationModel, RoleModel, ResponsibilityModel } from "../../lib/v1";'
|
||||
: 'import { Enterprise, DomainModel, IndustryModel, ProfessionModel, FieldModel, RoleModel, TaskModel } from "../../lib/v2";';
|
||||
|
||||
return `
|
||||
Generate ONLY TypeScript code for a professional hierarchy in the ${params.domain} domain using the ${template.version} structure.
|
||||
|
||||
Structure: ${structureDescription}
|
||||
Complexity Level: ${params.complexity}
|
||||
${params.includeSkills ? '✓ Include relevant skills and competencies' : ''}
|
||||
${params.includeTools ? '✓ Include tools and technologies' : ''}
|
||||
${params.includeExamples ? '✓ Include practical examples and use cases' : ''}
|
||||
|
||||
IMPORTANT: Output ONLY valid TypeScript code. Do not include any markdown, explanations, or comments outside of TypeScript comments.
|
||||
|
||||
Requirements:
|
||||
1. Start with the import statement: ${importStatement}
|
||||
2. Create a realistic, comprehensive hierarchy using MobX State Tree models
|
||||
3. Use appropriate professional terminology
|
||||
4. Ensure logical relationships between levels
|
||||
5. ${params.complexity === 'complex' ? 'Include multiple branches and detailed attributes' :
|
||||
params.complexity === 'medium' ? 'Include moderate detail with key attributes' :
|
||||
'Keep it simple with essential elements only'}
|
||||
|
||||
The code should demonstrate:
|
||||
- Creating the hierarchy structure using the imported models
|
||||
- Adding relevant attributes (skills, tools, examples)
|
||||
- Basic operations (create, read, update)
|
||||
- Real-world application examples as TypeScript code
|
||||
|
||||
Output format: Pure TypeScript code only, no markdown or explanations.
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
export default HierarchyGenerator;
|
278
lib/components/output-formatter.ts
Normal file
278
lib/components/output-formatter.ts
Normal file
@@ -0,0 +1,278 @@
|
||||
export interface OutputOptions {
|
||||
format: 'typescript' | 'markdown' | 'json' | 'yaml';
|
||||
includeMetadata: boolean;
|
||||
includeTimestamp: boolean;
|
||||
includeComments: boolean;
|
||||
}
|
||||
|
||||
export interface FormattedOutput {
|
||||
content: string;
|
||||
filename: string;
|
||||
extension: string;
|
||||
metadata?: any;
|
||||
}
|
||||
|
||||
export class OutputFormatter {
|
||||
formatOutput(
|
||||
content: string,
|
||||
domain: string,
|
||||
version: 'v1' | 'v2',
|
||||
options: OutputOptions
|
||||
): FormattedOutput {
|
||||
switch (options.format) {
|
||||
case 'typescript':
|
||||
return this.formatTypeScript(content, domain, version, options);
|
||||
case 'markdown':
|
||||
return this.formatMarkdown(content, domain, version, options);
|
||||
case 'json':
|
||||
return this.formatJSON(content, domain, version, options);
|
||||
case 'yaml':
|
||||
return this.formatYAML(content, domain, version, options);
|
||||
default:
|
||||
throw new Error(`Unsupported format: ${options.format}`);
|
||||
}
|
||||
}
|
||||
|
||||
private formatTypeScript(
|
||||
content: string,
|
||||
domain: string,
|
||||
version: 'v1' | 'v2',
|
||||
options: OutputOptions
|
||||
): FormattedOutput {
|
||||
const header = options.includeComments ? `/**
|
||||
* ${domain} Professional Hierarchy Example
|
||||
* Generated using OpenAI Agents SDK and Sumpin Professional Hierarchy Models
|
||||
* Model Version: ${version} (${version === 'v1' ? '4-layer' : '6-layer'} hierarchy)
|
||||
* ${options.includeTimestamp ? `Generated on: ${new Date().toISOString()}` : ''}
|
||||
*/
|
||||
|
||||
` : '';
|
||||
|
||||
const imports = `import {
|
||||
Enterprise,
|
||||
${version === 'v1' ? 'DomainModel, SpecializationModel, RoleModel, ResponsibilityModel' : 'DomainModel, IndustryModel, ProfessionModel, FieldModel, RoleModel, TaskModel'}
|
||||
} from "../../lib/${version}";
|
||||
|
||||
`;
|
||||
|
||||
const cleanedContent = this.extractTypeScriptCode(content);
|
||||
|
||||
return {
|
||||
content: header + imports + cleanedContent,
|
||||
filename: `${domain.toLowerCase()}-hierarchy-example`,
|
||||
extension: 'ts',
|
||||
metadata: {
|
||||
domain,
|
||||
version,
|
||||
generatedAt: options.includeTimestamp ? new Date().toISOString() : undefined
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private extractTypeScriptCode(content: string): string {
|
||||
let cleaned = content;
|
||||
|
||||
cleaned = cleaned.replace(/^#{1,6}\s+.*$/gm, '');
|
||||
|
||||
cleaned = cleaned.replace(/\*\*([^*]+)\*\*/g, '$1');
|
||||
cleaned = cleaned.replace(/\*([^*]+)\*/g, '$1');
|
||||
|
||||
// Remove markdown lists that aren't TypeScript code
|
||||
cleaned = cleaned.replace(/^[\s]*[-*+]\s+\*\*([^*]+)\*\*$/gm, '');
|
||||
cleaned = cleaned.replace(/^[\s]*[-*+]\s+([^:]+):$/gm, '');
|
||||
|
||||
// Extract TypeScript code blocks
|
||||
const codeBlockRegex = /```typescript\s*([\s\S]*?)```/g;
|
||||
const codeBlocks = [];
|
||||
let match;
|
||||
|
||||
while ((match = codeBlockRegex.exec(content)) !== null) {
|
||||
codeBlocks.push(match[1].trim());
|
||||
}
|
||||
|
||||
// If we found code blocks, use them
|
||||
if (codeBlocks.length > 0) {
|
||||
return codeBlocks.join('\n\n');
|
||||
}
|
||||
|
||||
// Otherwise, try to extract TypeScript-like content
|
||||
const lines = cleaned.split('\n');
|
||||
const tsLines = [];
|
||||
let inCodeSection = false;
|
||||
|
||||
for (const line of lines) {
|
||||
const trimmed = line.trim();
|
||||
|
||||
// Skip empty lines and markdown-like content
|
||||
if (!trimmed ||
|
||||
trimmed.startsWith('#') ||
|
||||
trimmed.startsWith('*') ||
|
||||
trimmed.startsWith('-') ||
|
||||
trimmed.includes('Below is') ||
|
||||
trimmed.includes('Here\'s') ||
|
||||
trimmed.includes('TypeScript Code') ||
|
||||
trimmed.includes('Professional Hierarchy')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Look for TypeScript patterns
|
||||
if (trimmed.includes('interface ') ||
|
||||
trimmed.includes('class ') ||
|
||||
trimmed.includes('type ') ||
|
||||
trimmed.includes('const ') ||
|
||||
trimmed.includes('let ') ||
|
||||
trimmed.includes('var ') ||
|
||||
trimmed.includes('function ') ||
|
||||
trimmed.includes('export ') ||
|
||||
trimmed.includes('import ') ||
|
||||
trimmed.includes('{') ||
|
||||
trimmed.includes('}') ||
|
||||
trimmed.includes(';') ||
|
||||
inCodeSection) {
|
||||
|
||||
tsLines.push(line);
|
||||
inCodeSection = true;
|
||||
|
||||
// End code section on certain patterns
|
||||
if (trimmed === '}' && !line.includes(',')) {
|
||||
inCodeSection = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return tsLines.join('\n').trim() || cleaned.trim();
|
||||
}
|
||||
|
||||
private formatMarkdown(
|
||||
content: string,
|
||||
domain: string,
|
||||
version: 'v1' | 'v2',
|
||||
options: OutputOptions
|
||||
): FormattedOutput {
|
||||
const header = `# ${domain.charAt(0).toUpperCase() + domain.slice(1)} Professional Hierarchy Example
|
||||
|
||||
Generated using OpenAI Agents SDK and Sumpin Professional Hierarchy Models
|
||||
|
||||
## Overview
|
||||
|
||||
This example demonstrates a ${version} professional hierarchy model for the ${domain} domain.
|
||||
|
||||
**Model Version:** ${version} (${version === 'v1' ? '4-layer' : '6-layer'} hierarchy)
|
||||
${options.includeTimestamp ? `**Generated on:** ${new Date().toISOString()}` : ''}
|
||||
|
||||
## Structure
|
||||
|
||||
${version === 'v1' ? 'Domain → Specialization → Role → Responsibility' : 'Domain → Industry → Profession → Field → Role → Task'}
|
||||
|
||||
## Generated Content
|
||||
|
||||
\`\`\`typescript
|
||||
${content}
|
||||
\`\`\`
|
||||
|
||||
## Usage
|
||||
|
||||
To use this example:
|
||||
|
||||
1. Ensure you have the required dependencies installed:
|
||||
\`\`\`bash
|
||||
bun add mobx-state-tree mobx uuid
|
||||
bun add -d @types/uuid
|
||||
\`\`\`
|
||||
|
||||
2. Run the example:
|
||||
\`\`\`bash
|
||||
bun run examples/generated/${domain.toLowerCase()}-hierarchy-example.ts
|
||||
\`\`\`
|
||||
|
||||
---
|
||||
*This example was generated automatically and demonstrates best practices for professional hierarchy modeling.*
|
||||
`;
|
||||
|
||||
return {
|
||||
content: header,
|
||||
filename: `${domain.toLowerCase()}-hierarchy-example`,
|
||||
extension: 'md',
|
||||
metadata: {
|
||||
domain,
|
||||
version,
|
||||
generatedAt: options.includeTimestamp ? new Date().toISOString() : undefined
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private formatJSON(
|
||||
content: string,
|
||||
domain: string,
|
||||
version: 'v1' | 'v2',
|
||||
options: OutputOptions
|
||||
): FormattedOutput {
|
||||
const data = {
|
||||
domain,
|
||||
version,
|
||||
structure: version === 'v1'
|
||||
? ['Domain', 'Specialization', 'Role', 'Responsibility']
|
||||
: ['Domain', 'Industry', 'Profession', 'Field', 'Role', 'Task'],
|
||||
generatedContent: content,
|
||||
...(options.includeMetadata && {
|
||||
metadata: {
|
||||
generatedAt: options.includeTimestamp ? new Date().toISOString() : undefined,
|
||||
generator: 'OpenAI Agents SDK + Sumpin',
|
||||
hierarchyType: `${version === 'v1' ? '4' : '6'}-layer hierarchy`
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
return {
|
||||
content: JSON.stringify(data, null, 2),
|
||||
filename: `${domain.toLowerCase()}-hierarchy-example`,
|
||||
extension: 'json',
|
||||
metadata: data.metadata
|
||||
};
|
||||
}
|
||||
|
||||
private formatYAML(
|
||||
content: string,
|
||||
domain: string,
|
||||
version: 'v1' | 'v2',
|
||||
options: OutputOptions
|
||||
): FormattedOutput {
|
||||
const yamlContent = `domain: ${domain}
|
||||
version: ${version}
|
||||
structure:
|
||||
${(version === 'v1'
|
||||
? ['Domain', 'Specialization', 'Role', 'Responsibility']
|
||||
: ['Domain', 'Industry', 'Profession', 'Field', 'Role', 'Task']
|
||||
).map(item => ` - ${item}`).join('\n')}
|
||||
|
||||
generated_content: |
|
||||
${content.split('\n').map(line => ` ${line}`).join('\n')}
|
||||
|
||||
${options.includeMetadata ? `metadata:
|
||||
generated_at: ${options.includeTimestamp ? new Date().toISOString() : 'null'}
|
||||
generator: "OpenAI Agents SDK + Sumpin"
|
||||
hierarchy_type: "${version === 'v1' ? '4' : '6'}-layer hierarchy"` : ''}`;
|
||||
|
||||
return {
|
||||
content: yamlContent,
|
||||
filename: `${domain.toLowerCase()}-hierarchy-example`,
|
||||
extension: 'yaml',
|
||||
metadata: {
|
||||
domain,
|
||||
version,
|
||||
generatedAt: options.includeTimestamp ? new Date().toISOString() : undefined
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
getDefaultOptions(): OutputOptions {
|
||||
return {
|
||||
format: 'typescript',
|
||||
includeMetadata: true,
|
||||
includeTimestamp: true,
|
||||
includeComments: true
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default OutputFormatter;
|
81
lib/components/template-manager.ts
Normal file
81
lib/components/template-manager.ts
Normal file
@@ -0,0 +1,81 @@
|
||||
import { HierarchyTemplate } from './hierarchy-generator';
|
||||
import v1Finance from "./templates/v1-finance.ts";
|
||||
import v1Tech from "./templates/v1-tech.ts";
|
||||
import v2Edu from "./templates/v2-edu.ts";
|
||||
import v2Healthcare from "./templates/v2-healthcare.ts";
|
||||
import v2Tech from "./templates/v2-tech.ts";
|
||||
|
||||
export interface DomainTemplate extends HierarchyTemplate {
|
||||
domain: string;
|
||||
commonSkills: string[];
|
||||
commonTools: string[];
|
||||
examples: string[];
|
||||
}
|
||||
|
||||
export class TemplateManager {
|
||||
private templates: Map<string, DomainTemplate> = new Map();
|
||||
|
||||
constructor() {
|
||||
this.initializeDefaultTemplates();
|
||||
}
|
||||
|
||||
private initializeDefaultTemplates() {
|
||||
// V2 Templates (6-layer hierarchy)
|
||||
this.addTemplate('technology-v2', v2Tech);
|
||||
this.addTemplate('healthcare-v2', v2Healthcare);
|
||||
this.addTemplate('education-v2', v2Edu);
|
||||
|
||||
// V1 Templates (4-layer hierarchy)
|
||||
this.addTemplate('technology-v1', v1Tech);
|
||||
this.addTemplate('finance-v1', v1Finance);
|
||||
}
|
||||
|
||||
addTemplate(key: string, template: DomainTemplate): void {
|
||||
this.templates.set(key, template);
|
||||
}
|
||||
|
||||
getTemplate(key: string): DomainTemplate | undefined {
|
||||
return this.templates.get(key);
|
||||
}
|
||||
|
||||
getTemplatesByVersion(version: 'v1' | 'v2'): DomainTemplate[] {
|
||||
return Array.from(this.templates.values()).filter(t => t.version === version);
|
||||
}
|
||||
|
||||
getTemplatesByDomain(domain: string): DomainTemplate[] {
|
||||
return Array.from(this.templates.values()).filter(t =>
|
||||
t.domain.toLowerCase().includes(domain.toLowerCase())
|
||||
);
|
||||
}
|
||||
|
||||
getAllTemplates(): DomainTemplate[] {
|
||||
return Array.from(this.templates.values());
|
||||
}
|
||||
|
||||
createCustomTemplate(
|
||||
key: string,
|
||||
domain: string,
|
||||
version: 'v1' | 'v2',
|
||||
options: Partial<DomainTemplate> = {}
|
||||
): DomainTemplate {
|
||||
const structure = version === 'v1'
|
||||
? ['Domain', 'Specialization', 'Role', 'Responsibility']
|
||||
: ['Domain', 'Industry', 'Profession', 'Field', 'Role', 'Task'];
|
||||
|
||||
const template: DomainTemplate = {
|
||||
version,
|
||||
domain,
|
||||
structure,
|
||||
description: `${domain} professional hierarchy`,
|
||||
commonSkills: [],
|
||||
commonTools: [],
|
||||
examples: [],
|
||||
...options
|
||||
};
|
||||
|
||||
this.addTemplate(key, template);
|
||||
return template;
|
||||
}
|
||||
}
|
||||
|
||||
export default TemplateManager;
|
9
lib/components/templates/v1-finance.ts
Normal file
9
lib/components/templates/v1-finance.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
export default {
|
||||
version: 'v1',
|
||||
domain: 'Finance',
|
||||
structure: ['Domain', 'Specialization', 'Role', 'Responsibility'],
|
||||
description: 'Simplified finance sector hierarchy',
|
||||
commonSkills: ['Financial Analysis', 'Risk Assessment', 'Compliance', 'Reporting'],
|
||||
commonTools: ['Excel', 'Financial Software', 'Analytics Tools', 'Reporting Systems'],
|
||||
examples: ['Investment Banking', 'Corporate Finance', 'Risk Management', 'Accounting']
|
||||
}
|
9
lib/components/templates/v1-tech.ts
Normal file
9
lib/components/templates/v1-tech.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
export default {
|
||||
version: 'v1',
|
||||
domain: 'Technology',
|
||||
structure: ['Domain', 'Specialization', 'Role', 'Responsibility'],
|
||||
description: 'Simplified technology sector hierarchy',
|
||||
commonSkills: ['Programming', 'System Design', 'Testing', 'Documentation'],
|
||||
commonTools: ['Code Editors', 'Version Control', 'Build Tools', 'Monitoring'],
|
||||
examples: ['Web Development', 'Mobile Development', 'Backend Systems', 'Frontend UI']
|
||||
}
|
9
lib/components/templates/v2-edu.ts
Normal file
9
lib/components/templates/v2-edu.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
export default {
|
||||
version: 'v2',
|
||||
domain: 'Education',
|
||||
structure: ['Domain', 'Industry', 'Profession', 'Field', 'Role', 'Task'],
|
||||
description: 'Comprehensive education sector hierarchy',
|
||||
commonSkills: ['Teaching', 'Curriculum Development', 'Assessment', 'Student Engagement'],
|
||||
commonTools: ['LMS', 'Educational Software', 'Assessment Tools', 'Communication Platforms'],
|
||||
examples: ['K-12 Education', 'Higher Education', 'Corporate Training', 'Online Learning']
|
||||
}
|
9
lib/components/templates/v2-healthcare.ts
Normal file
9
lib/components/templates/v2-healthcare.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
export default {
|
||||
version: 'v2',
|
||||
domain: 'Healthcare',
|
||||
structure: ['Domain', 'Industry', 'Profession', 'Field', 'Role', 'Task'],
|
||||
description: 'Comprehensive healthcare sector hierarchy',
|
||||
commonSkills: ['Patient Care', 'Medical Knowledge', 'Communication', 'Empathy'],
|
||||
commonTools: ['EMR Systems', 'Medical Devices', 'Diagnostic Tools', 'Communication Systems'],
|
||||
examples: ['Clinical Care', 'Medical Research', 'Healthcare Administration', 'Public Health']
|
||||
}
|
9
lib/components/templates/v2-tech.ts
Normal file
9
lib/components/templates/v2-tech.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
export default {
|
||||
version: 'v2',
|
||||
domain: 'Technology',
|
||||
structure: ['Domain', 'Industry', 'Profession', 'Field', 'Role', 'Task'],
|
||||
description: 'Comprehensive technology sector hierarchy',
|
||||
commonSkills: ['Problem Solving', 'Critical Thinking', 'Communication', 'Collaboration'],
|
||||
commonTools: ['Git', 'IDE', 'Testing Frameworks', 'Documentation Tools'],
|
||||
examples: ['Software Development', 'DevOps', 'Data Science', 'Cybersecurity']
|
||||
}
|
35
lib/hierarchy-model.ts
Normal file
35
lib/hierarchy-model.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import { types } from "mobx-state-tree";
|
||||
|
||||
/**
|
||||
* Reusable abstraction for a domain/sector hierarchy.
|
||||
* Works for both v1 and v2
|
||||
*/
|
||||
export const HierarchyModel = types.model("HierarchyModel", {
|
||||
version: types.string, // "v1" | "v2"
|
||||
domain: types.string, // e.g. "Finance", "Technology"
|
||||
structure: types.array(types.string), // ordered hierarchy labels
|
||||
description: types.string, // plain-text description
|
||||
commonSkills: types.array(types.string),
|
||||
commonTools: types.array(types.string),
|
||||
examples: types.array(types.string)
|
||||
});
|
||||
|
||||
/* ------------------------------------------------------------------ *
|
||||
* Example usage
|
||||
* ------------------------------------------------------------------ */
|
||||
|
||||
// Create individual instances
|
||||
// import { HierarchyModel } from "./hierarchy-model";
|
||||
//
|
||||
// const finance = HierarchyModel.create(v1Finance);
|
||||
// const tech = HierarchyModel.create(v2Tech);
|
||||
|
||||
export const HierarchyStore = types
|
||||
.model("HierarchyStore", {
|
||||
items: types.map(HierarchyModel) // keyed by domain name
|
||||
})
|
||||
.actions(self => ({
|
||||
add(h: Instance<typeof HierarchyModel>) {
|
||||
self.items.set(h.domain, h);
|
||||
}
|
||||
}));
|
4
lib/index.ts
Normal file
4
lib/index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
import v1 from './v1';
|
||||
import v2 from './v2';
|
||||
|
||||
export { v1, v2 };
|
41
lib/v1.ts
Normal file
41
lib/v1.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import { types } from "mobx-state-tree"
|
||||
|
||||
const Attribute = types.model("Attribute", {
|
||||
name: types.string,
|
||||
type: types.enumeration("AttributeType", ["Skill", "Tool", "Trait"]),
|
||||
description: types.optional(types.string, "")
|
||||
})
|
||||
|
||||
const Responsibility = types.model("Responsibility", {
|
||||
title: types.string,
|
||||
outcome: types.string,
|
||||
requiredAttributes: types.array(Attribute)
|
||||
})
|
||||
|
||||
const Role = types.model("Role", {
|
||||
title: types.string,
|
||||
responsibilities: types.array(Responsibility),
|
||||
requiredAttributes: types.array(Attribute),
|
||||
seniority: types.enumeration("Seniority", ["Intern", "Junior", "Mid", "Senior", "Lead", "Principal"])
|
||||
})
|
||||
|
||||
// Specialization within a domain (e.g., cardiologist within medicine)
|
||||
const Specialization = types.model("Specialization", {
|
||||
name: types.string,
|
||||
focus: types.string,
|
||||
coreAttributes: types.array(Attribute),
|
||||
roles: types.array(Role)
|
||||
})
|
||||
|
||||
const Domain = types.model("Domain", {
|
||||
name: types.string,
|
||||
description: types.optional(types.string, ""),
|
||||
specializations: types.array(Specialization),
|
||||
coreAttributes: types.array(Attribute)
|
||||
})
|
||||
|
||||
const ProfessionModel = types.model("ProfessionModel", {
|
||||
domains: types.array(Domain)
|
||||
});
|
||||
|
||||
export { ProfessionModel, Domain, Specialization, Role, Responsibility, Attribute }
|
194
lib/v2.ts
Normal file
194
lib/v2.ts
Normal file
@@ -0,0 +1,194 @@
|
||||
// Profession Hierarchy Model using mobx-state-tree (MST)
|
||||
// -----------------------------------------------------
|
||||
// This file defines a generic, extensible hierarchy that captures the essence
|
||||
// of professions. It intentionally separates concerns across six conceptual
|
||||
// layers, from the broad Domain level down to atomic Tasks. Each layer owns
|
||||
// its children through strongly‑typed arrays, enabling fine‑grained reactivity,
|
||||
// traversal, and manipulation.
|
||||
//
|
||||
// Layering
|
||||
// ┌ Domain – e.g. "STEM", "Arts", "Public Service"
|
||||
// │ └ Industry – e.g. "Software", "Healthcare", "Finance"
|
||||
// │ └ Profession – e.g. "Software Engineering", "Nursing"
|
||||
// │ └ Field – e.g. "Backend", "Pediatrics"
|
||||
// │ └ Role – e.g. "API Engineer", "Pediatric Nurse"
|
||||
// │ └ Task – e.g. "Design REST endpoints", "Administer vaccine"
|
||||
//
|
||||
// Each model exposes actions to mutate its children as well as compositional
|
||||
// views to rapidly surface nested information (e.g., all tasks under an
|
||||
// Industry). Identifiers are UUID‑v4 strings to guarantee uniqueness across
|
||||
// distributed systems.
|
||||
// -----------------------------------------------------
|
||||
|
||||
import { types, Instance, SnapshotIn, getParent, destroy } from "mobx-state-tree";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
|
||||
/* Utility --------------------------------------------------------------- */
|
||||
const withId = <T extends Record<string, unknown>>(modelName: string, definition: T) =>
|
||||
types.model(modelName, {
|
||||
id: types.optional(types.identifier, uuidv4),
|
||||
...definition
|
||||
});
|
||||
|
||||
/* Task ------------------------------------------------------------------ */
|
||||
export const TaskModel = withId("Task", {
|
||||
name: types.string,
|
||||
description: types.maybe(types.string)
|
||||
})
|
||||
.actions(self => ({
|
||||
update(attrs: Partial<SnapshotIn<typeof TaskModel>>) {
|
||||
Object.assign(self, attrs);
|
||||
},
|
||||
remove() {
|
||||
destroy(self);
|
||||
}
|
||||
}));
|
||||
export interface Task extends Instance<typeof TaskModel> {}
|
||||
|
||||
/* Role ------------------------------------------------------------------ */
|
||||
export const RoleModel = withId("Role", {
|
||||
title: types.string,
|
||||
summary: types.maybe(types.string),
|
||||
tasks: types.optional(types.array(TaskModel), [])
|
||||
})
|
||||
.actions(self => ({
|
||||
addTask(task: SnapshotIn<typeof TaskModel>) {
|
||||
self.tasks.push(TaskModel.create(task));
|
||||
},
|
||||
removeTask(task: Task) {
|
||||
self.tasks.remove(task);
|
||||
},
|
||||
remove() {
|
||||
destroy(self);
|
||||
}
|
||||
}))
|
||||
.views(self => ({
|
||||
get allTasks() {
|
||||
return self.tasks.slice();
|
||||
}
|
||||
}));
|
||||
export interface Role extends Instance<typeof RoleModel> {}
|
||||
|
||||
/* Field (Specialization) ------------------------------------------------- */
|
||||
export const FieldModel = withId("Field", {
|
||||
name: types.string,
|
||||
description: types.maybe(types.string),
|
||||
roles: types.optional(types.array(RoleModel), [])
|
||||
})
|
||||
.actions(self => ({
|
||||
addRole(role: SnapshotIn<typeof RoleModel>) {
|
||||
self.roles.push(RoleModel.create(role));
|
||||
},
|
||||
removeRole(role: Role) {
|
||||
self.roles.remove(role);
|
||||
},
|
||||
remove() {
|
||||
destroy(self);
|
||||
}
|
||||
}))
|
||||
.views(self => ({
|
||||
get allTasks() {
|
||||
return self.roles.flatMap(r => r.allTasks);
|
||||
}
|
||||
}));
|
||||
export interface Field extends Instance<typeof FieldModel> {}
|
||||
|
||||
/* Profession ------------------------------------------------------------ */
|
||||
export const ProfessionModel = withId("Profession", {
|
||||
name: types.string,
|
||||
description: types.maybe(types.string),
|
||||
fields: types.optional(types.array(FieldModel), [])
|
||||
})
|
||||
.actions(self => ({
|
||||
addField(field: SnapshotIn<typeof FieldModel>) {
|
||||
self.fields.push(FieldModel.create(field));
|
||||
},
|
||||
removeField(field: Field) {
|
||||
self.fields.remove(field);
|
||||
},
|
||||
remove() {
|
||||
destroy(self);
|
||||
}
|
||||
}))
|
||||
.views(self => ({
|
||||
get allTasks() {
|
||||
return self.fields.flatMap(f => f.allTasks);
|
||||
}
|
||||
}));
|
||||
export interface Profession extends Instance<typeof ProfessionModel> {}
|
||||
|
||||
/* Industry -------------------------------------------------------------- */
|
||||
export const IndustryModel = withId("Industry", {
|
||||
name: types.string,
|
||||
description: types.maybe(types.string),
|
||||
professions: types.optional(types.array(ProfessionModel), [])
|
||||
})
|
||||
.actions(self => ({
|
||||
addProfession(prof: SnapshotIn<typeof ProfessionModel>) {
|
||||
self.professions.push(ProfessionModel.create(prof));
|
||||
},
|
||||
removeProfession(prof: Profession) {
|
||||
self.professions.remove(prof);
|
||||
},
|
||||
remove() {
|
||||
destroy(self);
|
||||
}
|
||||
}))
|
||||
.views(self => ({
|
||||
get allTasks() {
|
||||
return self.professions.flatMap(p => p.allTasks);
|
||||
}
|
||||
}));
|
||||
export interface Industry extends Instance<typeof IndustryModel> {}
|
||||
|
||||
/* Domain ---------------------------------------------------------------- */
|
||||
export const DomainModel = withId("Domain", {
|
||||
name: types.string,
|
||||
description: types.maybe(types.string),
|
||||
industries: types.optional(types.array(IndustryModel), [])
|
||||
})
|
||||
.actions(self => ({
|
||||
addIndustry(ind: SnapshotIn<typeof IndustryModel>) {
|
||||
self.industries.push(IndustryModel.create(ind));
|
||||
},
|
||||
removeIndustry(ind: Industry) {
|
||||
self.industries.remove(ind);
|
||||
},
|
||||
remove() {
|
||||
destroy(self);
|
||||
}
|
||||
}))
|
||||
.views(self => ({
|
||||
get allTasks() {
|
||||
return self.industries.flatMap(i => i.allTasks);
|
||||
}
|
||||
}));
|
||||
export interface Domain extends Instance<typeof DomainModel> {}
|
||||
|
||||
/* Enterprise ------------------------------------------------------------- */
|
||||
export const Enterprise = types
|
||||
.model("Enterprise", {
|
||||
domains: types.optional(types.array(DomainModel), [])
|
||||
})
|
||||
.actions(self => ({
|
||||
addDomain(domain: SnapshotIn<typeof DomainModel>) {
|
||||
self.domains.push(DomainModel.create(domain));
|
||||
}
|
||||
}))
|
||||
.views(self => ({
|
||||
// Convenience: get a flat list of everything
|
||||
get allTasks() {
|
||||
return self.domains.flatMap(d => d.allTasks);
|
||||
}
|
||||
}));
|
||||
export interface IRootStore extends Instance<typeof Enterprise> {}
|
||||
|
||||
/* Example usage --------------------------------------------------------- */
|
||||
// const store = Enterprise.create({});
|
||||
// store.addDomain({ name: "STEM" });
|
||||
// store.domains[0].addIndustry({ name: "Software" });
|
||||
// store.domains[0].industries[0].addProfession({ name: "Software Engineering" });
|
||||
// store.domains[0].industries[0].professions[0].addField({ name: "Backend" });
|
||||
// store.domains[0].industries[0].professions[0].fields[0].addRole({ title: "API Engineer" });
|
||||
// store.domains[0].industries[0].professions[0].fields[0].roles[0].addTask({ name: "Design REST endpoints" });
|
||||
// console.log(store.allTasks.map(t => t.name)); // → ["Design REST endpoints"]
|
Reference in New Issue
Block a user