@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.
Files changed (56) hide show
  1. package/README.md +1 -1
  2. package/bin/fit-map.js +12 -12
  3. package/package.json +9 -6
  4. package/schema/json/discipline.schema.json +2 -6
  5. package/schema/rdf/discipline.ttl +6 -19
  6. package/src/index-generator.js +67 -38
  7. package/src/index.js +10 -25
  8. package/src/loader.js +407 -562
  9. package/src/schema-validation.js +327 -307
  10. package/examples/behaviours/_index.yaml +0 -8
  11. package/examples/behaviours/outcome_ownership.yaml +0 -43
  12. package/examples/behaviours/polymathic_knowledge.yaml +0 -41
  13. package/examples/behaviours/precise_communication.yaml +0 -39
  14. package/examples/behaviours/relentless_curiosity.yaml +0 -37
  15. package/examples/behaviours/systems_thinking.yaml +0 -40
  16. package/examples/capabilities/_index.yaml +0 -8
  17. package/examples/capabilities/business.yaml +0 -205
  18. package/examples/capabilities/delivery.yaml +0 -1001
  19. package/examples/capabilities/people.yaml +0 -68
  20. package/examples/capabilities/reliability.yaml +0 -349
  21. package/examples/capabilities/scale.yaml +0 -1672
  22. package/examples/copilot-setup-steps.yaml +0 -25
  23. package/examples/devcontainer.yaml +0 -21
  24. package/examples/disciplines/_index.yaml +0 -6
  25. package/examples/disciplines/data_engineering.yaml +0 -68
  26. package/examples/disciplines/engineering_management.yaml +0 -61
  27. package/examples/disciplines/software_engineering.yaml +0 -68
  28. package/examples/drivers.yaml +0 -202
  29. package/examples/framework.yaml +0 -73
  30. package/examples/levels.yaml +0 -115
  31. package/examples/questions/behaviours/outcome_ownership.yaml +0 -228
  32. package/examples/questions/behaviours/polymathic_knowledge.yaml +0 -275
  33. package/examples/questions/behaviours/precise_communication.yaml +0 -248
  34. package/examples/questions/behaviours/relentless_curiosity.yaml +0 -248
  35. package/examples/questions/behaviours/systems_thinking.yaml +0 -238
  36. package/examples/questions/capabilities/business.yaml +0 -107
  37. package/examples/questions/capabilities/delivery.yaml +0 -101
  38. package/examples/questions/capabilities/people.yaml +0 -106
  39. package/examples/questions/capabilities/reliability.yaml +0 -105
  40. package/examples/questions/capabilities/scale.yaml +0 -104
  41. package/examples/questions/skills/architecture_design.yaml +0 -115
  42. package/examples/questions/skills/cloud_platforms.yaml +0 -105
  43. package/examples/questions/skills/code_quality.yaml +0 -162
  44. package/examples/questions/skills/data_modeling.yaml +0 -107
  45. package/examples/questions/skills/devops.yaml +0 -111
  46. package/examples/questions/skills/full_stack_development.yaml +0 -118
  47. package/examples/questions/skills/sre_practices.yaml +0 -113
  48. package/examples/questions/skills/stakeholder_management.yaml +0 -116
  49. package/examples/questions/skills/team_collaboration.yaml +0 -106
  50. package/examples/questions/skills/technical_writing.yaml +0 -110
  51. package/examples/self-assessments.yaml +0 -64
  52. package/examples/stages.yaml +0 -191
  53. package/examples/tracks/_index.yaml +0 -5
  54. package/examples/tracks/platform.yaml +0 -47
  55. package/examples/tracks/sre.yaml +0 -46
  56. package/examples/vscode-settings.yaml +0 -21
package/README.md CHANGED
@@ -64,4 +64,4 @@ examples/
64
64
  └── questions/ # Interview questions
65
65
  ```
66
66
 
67
- See the [documentation](../../docs/map/index.md) for details.
67
+ See the [documentation](../../website/docs/map/index.md) for details.
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 { runSchemaValidation, loadAllData } = await import("../src/index.js");
127
+ const { createDataLoader, createSchemaValidator } =
128
+ await import("../src/index.js");
128
129
 
129
- // Load data first
130
- const data = await loadAllData(dataDir, { validate: false });
130
+ const loader = createDataLoader();
131
+ const validator = createSchemaValidator();
131
132
 
132
- // Run full validation
133
- const result = await runSchemaValidation(dataDir, data);
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 { generateAllIndexes } = await import("../src/index.js");
155
+ const { createIndexGenerator } = await import("../src/index.js");
156
156
 
157
- const results = await generateAllIndexes(dataDir);
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/ingestion/people.js");
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.12.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/ingestion/people": "./activity/ingestion/people.js",
42
- "./activity/ingestion/getdx": "./activity/ingestion/getdx.js",
43
- "./activity/ingestion/github": "./activity/ingestion/github.js"
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
- "professionalRoleSummary": {
91
+ "roleSummary": {
92
92
  "type": "string",
93
- "description": "Role summary for professional/IC track. May use {roleTitle} placeholder."
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:professionalRoleSummary a rdf:Property ;
90
- rdfs:label "professionalRoleSummary"@en ;
91
- rdfs:comment "Role summary for professional/IC track. May use {roleTitle} placeholder."@en ;
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:professionalRoleSummary ;
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 "managementRoleSummary" ;
258
- sh:description "Role summary for management track" ;
244
+ sh:name "roleSummary" ;
245
+ sh:description "Role summary for this discipline" ;
259
246
  ] .
260
247
 
261
248
  # -----------------------------------------------------------------------------
@@ -10,56 +10,85 @@ import { join, basename } from "path";
10
10
  import { stringify as stringifyYaml } from "yaml";
11
11
 
12
12
  /**
13
- * Generate _index.yaml for a directory
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 async function generateDirIndex(dir) {
18
- const files = await readdir(dir);
19
- const yamlFiles = files.filter(
20
- (f) => f.endsWith(".yaml") && !f.startsWith("_"),
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
- const fileIds = yamlFiles.map((f) => basename(f, ".yaml")).sort();
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
- const content = stringifyYaml(
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
- // Add header comment
35
- const output = `# Auto-generated index for browser loading
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
- await writeFile(join(dir, "_index.yaml"), output, "utf-8");
56
+ await this.#fs.writeFile(join(dir, "_index.yaml"), output, "utf-8");
40
57
 
41
- return fileIds;
42
- }
58
+ return fileIds;
59
+ }
43
60
 
44
- /**
45
- * Generate all index files for the data directory
46
- * @param {string} dataDir - Path to the data directory
47
- * @returns {Promise<Object>} Summary of generated indexes
48
- */
49
- export async function generateAllIndexes(dataDir) {
50
- const directories = ["behaviours", "disciplines", "tracks", "capabilities"];
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
- const results = {};
69
+ const results = {};
53
70
 
54
- for (const dir of directories) {
55
- const fullPath = join(dataDir, dir);
56
- try {
57
- const files = await generateDirIndex(fullPath);
58
- results[dir] = files;
59
- } catch (err) {
60
- results[dir] = { error: err.message };
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
- return results;
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
- // Data loading
8
- export {
9
- loadAllData,
10
- loadYamlFile,
11
- createDataLoader,
12
- loadFrameworkConfig,
13
- loadQuestionFolder,
14
- loadQuestionBankFromFolder,
15
- loadSelfAssessments,
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
- // Referential integrity validation
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