@forwardimpact/map 0.12.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/bin/fit-map.js +12 -12
- package/package.json +9 -6
- package/schema/json/discipline.schema.json +2 -6
- package/schema/rdf/discipline.ttl +6 -19
- package/src/index-generator.js +67 -38
- package/src/index.js +10 -25
- package/src/loader.js +407 -562
- package/src/schema-validation.js +327 -307
- package/examples/behaviours/_index.yaml +0 -8
- package/examples/behaviours/outcome_ownership.yaml +0 -43
- package/examples/behaviours/polymathic_knowledge.yaml +0 -41
- package/examples/behaviours/precise_communication.yaml +0 -39
- package/examples/behaviours/relentless_curiosity.yaml +0 -37
- package/examples/behaviours/systems_thinking.yaml +0 -40
- package/examples/capabilities/_index.yaml +0 -8
- package/examples/capabilities/business.yaml +0 -205
- package/examples/capabilities/delivery.yaml +0 -1001
- package/examples/capabilities/people.yaml +0 -68
- package/examples/capabilities/reliability.yaml +0 -349
- package/examples/capabilities/scale.yaml +0 -1672
- package/examples/copilot-setup-steps.yaml +0 -25
- package/examples/devcontainer.yaml +0 -21
- package/examples/disciplines/_index.yaml +0 -6
- package/examples/disciplines/data_engineering.yaml +0 -68
- package/examples/disciplines/engineering_management.yaml +0 -61
- package/examples/disciplines/software_engineering.yaml +0 -68
- package/examples/drivers.yaml +0 -202
- package/examples/framework.yaml +0 -73
- package/examples/levels.yaml +0 -115
- package/examples/questions/behaviours/outcome_ownership.yaml +0 -228
- package/examples/questions/behaviours/polymathic_knowledge.yaml +0 -275
- package/examples/questions/behaviours/precise_communication.yaml +0 -248
- package/examples/questions/behaviours/relentless_curiosity.yaml +0 -248
- package/examples/questions/behaviours/systems_thinking.yaml +0 -238
- package/examples/questions/capabilities/business.yaml +0 -107
- package/examples/questions/capabilities/delivery.yaml +0 -101
- package/examples/questions/capabilities/people.yaml +0 -106
- package/examples/questions/capabilities/reliability.yaml +0 -105
- package/examples/questions/capabilities/scale.yaml +0 -104
- package/examples/questions/skills/architecture_design.yaml +0 -115
- package/examples/questions/skills/cloud_platforms.yaml +0 -105
- package/examples/questions/skills/code_quality.yaml +0 -162
- package/examples/questions/skills/data_modeling.yaml +0 -107
- package/examples/questions/skills/devops.yaml +0 -111
- package/examples/questions/skills/full_stack_development.yaml +0 -118
- package/examples/questions/skills/sre_practices.yaml +0 -113
- package/examples/questions/skills/stakeholder_management.yaml +0 -116
- package/examples/questions/skills/team_collaboration.yaml +0 -106
- package/examples/questions/skills/technical_writing.yaml +0 -110
- package/examples/self-assessments.yaml +0 -64
- package/examples/stages.yaml +0 -191
- package/examples/tracks/_index.yaml +0 -5
- package/examples/tracks/platform.yaml +0 -47
- package/examples/tracks/sre.yaml +0 -46
- package/examples/vscode-settings.yaml +0 -21
package/README.md
CHANGED
package/bin/fit-map.js
CHANGED
|
@@ -72,11 +72,11 @@ async function findDataDir(providedPath) {
|
|
|
72
72
|
throw new Error(`Data directory not found: ${providedPath}`);
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
// Check common locations
|
|
76
75
|
const candidates = [
|
|
76
|
+
join(process.cwd(), "data/pathway"),
|
|
77
|
+
join(process.cwd(), "examples/pathway"),
|
|
77
78
|
join(process.cwd(), "data"),
|
|
78
79
|
join(process.cwd(), "examples"),
|
|
79
|
-
join(__dirname, "../examples"),
|
|
80
80
|
];
|
|
81
81
|
|
|
82
82
|
for (const candidate of candidates) {
|
|
@@ -124,17 +124,17 @@ function formatValidationResults(result) {
|
|
|
124
124
|
async function runValidate(dataDir) {
|
|
125
125
|
console.log(`🔍 Validating data in: ${dataDir}\n`);
|
|
126
126
|
|
|
127
|
-
const {
|
|
127
|
+
const { createDataLoader, createSchemaValidator } =
|
|
128
|
+
await import("../src/index.js");
|
|
128
129
|
|
|
129
|
-
|
|
130
|
-
const
|
|
130
|
+
const loader = createDataLoader();
|
|
131
|
+
const validator = createSchemaValidator();
|
|
131
132
|
|
|
132
|
-
|
|
133
|
-
const result = await
|
|
133
|
+
const data = await loader.loadAllData(dataDir);
|
|
134
|
+
const result = await validator.runFullValidation(dataDir, data);
|
|
134
135
|
|
|
135
136
|
console.log(formatValidationResults(result));
|
|
136
137
|
|
|
137
|
-
// Print summary
|
|
138
138
|
console.log("\n📊 Data Summary:");
|
|
139
139
|
console.log(` Skills: ${data.skills?.length || 0}`);
|
|
140
140
|
console.log(` Behaviours: ${data.behaviours?.length || 0}`);
|
|
@@ -152,9 +152,10 @@ async function runValidate(dataDir) {
|
|
|
152
152
|
async function runGenerateIndex(dataDir) {
|
|
153
153
|
console.log(`📁 Generating index files in: ${dataDir}\n`);
|
|
154
154
|
|
|
155
|
-
const {
|
|
155
|
+
const { createIndexGenerator } = await import("../src/index.js");
|
|
156
156
|
|
|
157
|
-
const
|
|
157
|
+
const generator = createIndexGenerator();
|
|
158
|
+
const results = await generator.generateAllIndexes(dataDir);
|
|
158
159
|
|
|
159
160
|
for (const [dir, files] of Object.entries(results)) {
|
|
160
161
|
if (files.error) {
|
|
@@ -179,7 +180,6 @@ async function runValidateShacl() {
|
|
|
179
180
|
const { default: N3 } = await import("n3");
|
|
180
181
|
const { readFile, readdir } = await import("fs/promises");
|
|
181
182
|
|
|
182
|
-
// Find all .ttl files in the RDF directory
|
|
183
183
|
const files = await readdir(rdfDir);
|
|
184
184
|
const ttlFiles = files.filter((f) => f.endsWith(".ttl")).sort();
|
|
185
185
|
|
|
@@ -229,7 +229,7 @@ async function runPeopleImport(filePath, dataDir) {
|
|
|
229
229
|
console.log(`👤 Importing people from: ${filePath}\n`);
|
|
230
230
|
|
|
231
231
|
const { loadPeopleFile, validatePeople } =
|
|
232
|
-
await import("../activity/
|
|
232
|
+
await import("../activity/transform/people.js");
|
|
233
233
|
|
|
234
234
|
const people = await loadPeopleFile(filePath);
|
|
235
235
|
console.log(` Loaded ${people.length} people from file`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@forwardimpact/map",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
4
4
|
"description": "Public data model for career frameworks, consumed by AI agents and engineers",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -20,7 +20,6 @@
|
|
|
20
20
|
},
|
|
21
21
|
"files": [
|
|
22
22
|
"bin/",
|
|
23
|
-
"examples/",
|
|
24
23
|
"src/",
|
|
25
24
|
"schema/"
|
|
26
25
|
],
|
|
@@ -31,16 +30,20 @@
|
|
|
31
30
|
"./schema-validation": "./src/schema-validation.js",
|
|
32
31
|
"./index-generator": "./src/index-generator.js",
|
|
33
32
|
"./levels": "./src/levels.js",
|
|
34
|
-
"./examples/*": "./examples/*",
|
|
35
33
|
"./schema/json/*": "./schema/json/*",
|
|
36
34
|
"./schema/rdf/*": "./schema/rdf/*",
|
|
37
35
|
"./activity/queries/org": "./activity/queries/org.js",
|
|
38
36
|
"./activity/queries/snapshots": "./activity/queries/snapshots.js",
|
|
39
37
|
"./activity/queries/evidence": "./activity/queries/evidence.js",
|
|
40
38
|
"./activity/queries/artifacts": "./activity/queries/artifacts.js",
|
|
41
|
-
"./activity/
|
|
42
|
-
"./activity/
|
|
43
|
-
"./activity/
|
|
39
|
+
"./activity/storage": "./activity/storage.js",
|
|
40
|
+
"./activity/extract/github": "./activity/extract/github.js",
|
|
41
|
+
"./activity/extract/getdx": "./activity/extract/getdx.js",
|
|
42
|
+
"./activity/extract/people": "./activity/extract/people.js",
|
|
43
|
+
"./activity/transform/github": "./activity/transform/github.js",
|
|
44
|
+
"./activity/transform/getdx": "./activity/transform/getdx.js",
|
|
45
|
+
"./activity/transform/people": "./activity/transform/people.js",
|
|
46
|
+
"./activity/transform": "./activity/transform/index.js"
|
|
44
47
|
},
|
|
45
48
|
"dependencies": {
|
|
46
49
|
"ajv": "^8.17.1",
|
|
@@ -88,13 +88,9 @@
|
|
|
88
88
|
"disciplineHumanSection": {
|
|
89
89
|
"type": "object",
|
|
90
90
|
"properties": {
|
|
91
|
-
"
|
|
91
|
+
"roleSummary": {
|
|
92
92
|
"type": "string",
|
|
93
|
-
"description": "Role summary for
|
|
94
|
-
},
|
|
95
|
-
"managementRoleSummary": {
|
|
96
|
-
"type": "string",
|
|
97
|
-
"description": "Role summary for management track. May use {specialization} placeholder."
|
|
93
|
+
"description": "Role summary for this discipline. May use {roleTitle} or {specialization} placeholder."
|
|
98
94
|
}
|
|
99
95
|
},
|
|
100
96
|
"additionalProperties": false
|
|
@@ -86,15 +86,9 @@ fit:behaviourModifiers a rdf:Property ;
|
|
|
86
86
|
rdfs:label "behaviourModifiers"@en ;
|
|
87
87
|
rdfs:comment "Modifiers to behaviour expectations"@en .
|
|
88
88
|
|
|
89
|
-
fit:
|
|
90
|
-
rdfs:label "
|
|
91
|
-
rdfs:comment "Role summary for
|
|
92
|
-
rdfs:domain fit:DisciplineHumanSection ;
|
|
93
|
-
rdfs:range xsd:string .
|
|
94
|
-
|
|
95
|
-
fit:managementRoleSummary a rdf:Property ;
|
|
96
|
-
rdfs:label "managementRoleSummary"@en ;
|
|
97
|
-
rdfs:comment "Role summary for management track. May use {specialization} placeholder."@en ;
|
|
89
|
+
fit:roleSummary a rdf:Property ;
|
|
90
|
+
rdfs:label "roleSummary"@en ;
|
|
91
|
+
rdfs:comment "Role summary for this discipline. May use {roleTitle} or {specialization} placeholder."@en ;
|
|
98
92
|
rdfs:domain fit:DisciplineHumanSection ;
|
|
99
93
|
rdfs:range xsd:string .
|
|
100
94
|
|
|
@@ -244,18 +238,11 @@ fit:DisciplineShape a sh:NodeShape ;
|
|
|
244
238
|
fit:DisciplineHumanSectionShape a sh:NodeShape ;
|
|
245
239
|
sh:targetClass fit:DisciplineHumanSection ;
|
|
246
240
|
sh:property [
|
|
247
|
-
sh:path fit:
|
|
248
|
-
sh:datatype xsd:string ;
|
|
249
|
-
sh:maxCount 1 ;
|
|
250
|
-
sh:name "professionalRoleSummary" ;
|
|
251
|
-
sh:description "Role summary for professional/IC track" ;
|
|
252
|
-
] ;
|
|
253
|
-
sh:property [
|
|
254
|
-
sh:path fit:managementRoleSummary ;
|
|
241
|
+
sh:path fit:roleSummary ;
|
|
255
242
|
sh:datatype xsd:string ;
|
|
256
243
|
sh:maxCount 1 ;
|
|
257
|
-
sh:name "
|
|
258
|
-
sh:description "Role summary for
|
|
244
|
+
sh:name "roleSummary" ;
|
|
245
|
+
sh:description "Role summary for this discipline" ;
|
|
259
246
|
] .
|
|
260
247
|
|
|
261
248
|
# -----------------------------------------------------------------------------
|
package/src/index-generator.js
CHANGED
|
@@ -10,56 +10,85 @@ import { join, basename } from "path";
|
|
|
10
10
|
import { stringify as stringifyYaml } from "yaml";
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
|
-
*
|
|
14
|
-
* @param {string} dir - Directory path
|
|
15
|
-
* @returns {Promise<string[]>} List of file IDs included
|
|
13
|
+
* Index generator class with injectable filesystem and serializer dependencies.
|
|
16
14
|
*/
|
|
17
|
-
export
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
15
|
+
export class IndexGenerator {
|
|
16
|
+
#fs;
|
|
17
|
+
#yaml;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @param {{ readdir: Function, writeFile: Function }} fs
|
|
21
|
+
* @param {{ stringify: Function }} yamlSerializer
|
|
22
|
+
*/
|
|
23
|
+
constructor(fs, yamlSerializer) {
|
|
24
|
+
if (!fs) throw new Error("fs is required");
|
|
25
|
+
if (!yamlSerializer) throw new Error("yamlSerializer is required");
|
|
26
|
+
this.#fs = fs;
|
|
27
|
+
this.#yaml = yamlSerializer;
|
|
28
|
+
}
|
|
22
29
|
|
|
23
|
-
|
|
30
|
+
/**
|
|
31
|
+
* Generate _index.yaml for a directory
|
|
32
|
+
* @param {string} dir - Directory path
|
|
33
|
+
* @returns {Promise<string[]>} List of file IDs included
|
|
34
|
+
*/
|
|
35
|
+
async generateDirIndex(dir) {
|
|
36
|
+
const files = await this.#fs.readdir(dir);
|
|
37
|
+
const yamlFiles = files.filter(
|
|
38
|
+
(f) => f.endsWith(".yaml") && !f.startsWith("_"),
|
|
39
|
+
);
|
|
24
40
|
|
|
25
|
-
|
|
26
|
-
{
|
|
27
|
-
// Auto-generated index for browser loading
|
|
28
|
-
// Do not edit manually - regenerate with: npx pathway --generate-index
|
|
29
|
-
files: fileIds,
|
|
30
|
-
},
|
|
31
|
-
{ lineWidth: 0 },
|
|
32
|
-
);
|
|
41
|
+
const fileIds = yamlFiles.map((f) => basename(f, ".yaml")).sort();
|
|
33
42
|
|
|
34
|
-
|
|
35
|
-
|
|
43
|
+
const content = this.#yaml.stringify(
|
|
44
|
+
{
|
|
45
|
+
// Auto-generated index for browser loading
|
|
46
|
+
// Do not edit manually - regenerate with: npx pathway --generate-index
|
|
47
|
+
files: fileIds,
|
|
48
|
+
},
|
|
49
|
+
{ lineWidth: 0 },
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
const output = `# Auto-generated index for browser loading
|
|
36
53
|
# Do not edit manually - regenerate with: npx pathway --generate-index
|
|
37
54
|
${content}`;
|
|
38
55
|
|
|
39
|
-
|
|
56
|
+
await this.#fs.writeFile(join(dir, "_index.yaml"), output, "utf-8");
|
|
40
57
|
|
|
41
|
-
|
|
42
|
-
}
|
|
58
|
+
return fileIds;
|
|
59
|
+
}
|
|
43
60
|
|
|
44
|
-
/**
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
61
|
+
/**
|
|
62
|
+
* Generate all index files for the data directory
|
|
63
|
+
* @param {string} dataDir - Path to the data directory
|
|
64
|
+
* @returns {Promise<Object>} Summary of generated indexes
|
|
65
|
+
*/
|
|
66
|
+
async generateAllIndexes(dataDir) {
|
|
67
|
+
const directories = ["behaviours", "disciplines", "tracks", "capabilities"];
|
|
51
68
|
|
|
52
|
-
|
|
69
|
+
const results = {};
|
|
53
70
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
71
|
+
for (const dir of directories) {
|
|
72
|
+
const fullPath = join(dataDir, dir);
|
|
73
|
+
try {
|
|
74
|
+
const files = await this.generateDirIndex(fullPath);
|
|
75
|
+
results[dir] = files;
|
|
76
|
+
} catch (err) {
|
|
77
|
+
results[dir] = { error: err.message };
|
|
78
|
+
}
|
|
61
79
|
}
|
|
80
|
+
|
|
81
|
+
return results;
|
|
62
82
|
}
|
|
83
|
+
}
|
|
63
84
|
|
|
64
|
-
|
|
85
|
+
/**
|
|
86
|
+
* Create an IndexGenerator with real filesystem and serializer dependencies
|
|
87
|
+
* @returns {IndexGenerator}
|
|
88
|
+
*/
|
|
89
|
+
export function createIndexGenerator() {
|
|
90
|
+
return new IndexGenerator(
|
|
91
|
+
{ readdir, writeFile },
|
|
92
|
+
{ stringify: stringifyYaml },
|
|
93
|
+
);
|
|
65
94
|
}
|
package/src/index.js
CHANGED
|
@@ -4,22 +4,17 @@
|
|
|
4
4
|
* Data model definitions, validation, and loading for Engineering Pathway.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
//
|
|
8
|
-
export {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
loadExampleData,
|
|
17
|
-
loadAndValidate,
|
|
18
|
-
loadAgentData,
|
|
19
|
-
loadSkillsWithAgentData,
|
|
20
|
-
} from "./loader.js";
|
|
7
|
+
// Classes
|
|
8
|
+
export { DataLoader } from "./loader.js";
|
|
9
|
+
export { SchemaValidator } from "./schema-validation.js";
|
|
10
|
+
export { IndexGenerator } from "./index-generator.js";
|
|
11
|
+
|
|
12
|
+
// Factory functions
|
|
13
|
+
export { createDataLoader } from "./loader.js";
|
|
14
|
+
export { createSchemaValidator } from "./schema-validation.js";
|
|
15
|
+
export { createIndexGenerator } from "./index-generator.js";
|
|
21
16
|
|
|
22
|
-
//
|
|
17
|
+
// Pure validation functions (unchanged)
|
|
23
18
|
export {
|
|
24
19
|
validateAllData,
|
|
25
20
|
validateQuestionBank,
|
|
@@ -27,16 +22,6 @@ export {
|
|
|
27
22
|
validateAgentData,
|
|
28
23
|
} from "./validation.js";
|
|
29
24
|
|
|
30
|
-
// Schema-based validation
|
|
31
|
-
export {
|
|
32
|
-
validateDataDirectory,
|
|
33
|
-
validateReferentialIntegrity,
|
|
34
|
-
runSchemaValidation,
|
|
35
|
-
} from "./schema-validation.js";
|
|
36
|
-
|
|
37
|
-
// Index generation
|
|
38
|
-
export { generateAllIndexes, generateDirIndex } from "./index-generator.js";
|
|
39
|
-
|
|
40
25
|
// Type constants and helpers
|
|
41
26
|
export * from "./levels.js";
|
|
42
27
|
|